Skip to main content

Terraform Data Sources

Data sources allow you to query existing Kubiya Control Plane resources and use their attributes in your Terraform configurations.

Why Use Data Sources?

  • Reference Existing Resources: Look up resources created outside Terraform
  • Cross-Stack References: Reference resources from other Terraform configurations
  • Dynamic Configuration: Build configurations based on existing infrastructure
  • Read-Only Access: Query resources without managing them

Available Data Sources

All managed resources have corresponding data sources:
  • controlplane_environment - Look up a single environment by ID
  • controlplane_project - Look up a single project by ID
  • controlplane_team - Look up a single team by ID
  • controlplane_agent - Look up a single agent by ID
  • controlplane_skill - Look up a single skill by ID
  • controlplane_policy - Look up a single policy by ID
  • controlplane_job - Look up a single job by ID
  • controlplane_jobs - List all jobs
  • controlplane_worker_queue - Look up a single worker queue by ID
  • controlplane_worker_queues - List all worker queues in an environment

Data Source Arguments

All data sources require the resource id:
data "controlplane_<resource>" "<name>" {
  id = "<resource-id>"
}

Data Source Examples

controlplane_environment

Look up an existing environment by ID:
data "controlplane_environment" "production" {
  id = "env-xxxxx"
}

# Use the data source
output "production_env_name" {
  value = data.controlplane_environment.production.name
}

output "production_env_config" {
  value = data.controlplane_environment.production.configuration
}

# Reference in other resources
resource "controlplane_agent" "new_agent" {
  name     = "agent-in-${data.controlplane_environment.production.name}"
  model_id = "kubiya/claude-sonnet-4"
  runtime  = "claude_code"

  configuration = jsonencode({
    environment = data.controlplane_environment.production.name
  })
}
Exported Attributes:
  • id - Environment ID
  • name - Environment name
  • display_name - Display name
  • description - Environment description
  • tags - List of tags
  • configuration - Environment configuration (JSON)
  • execution_environment - Execution settings (JSON)
  • created_at - Creation timestamp
  • updated_at - Last update timestamp

controlplane_project

Look up an existing project:
data "controlplane_project" "ml_platform" {
  id = "project-xxxxx"
}

output "project_info" {
  value = {
    name = data.controlplane_project.ml_platform.name
    key  = data.controlplane_project.ml_platform.key
  }
}

# Use in agent configuration
resource "controlplane_agent" "ml_agent" {
  name     = "ml-agent"
  model_id = "kubiya/claude-sonnet-4"
  runtime  = "claude_code"

  configuration = jsonencode({
    project = data.controlplane_project.ml_platform.name
  })
}
Exported Attributes:
  • id - Project ID
  • name - Project name
  • key - Project key
  • description - Project description
  • goals - Project goals
  • visibility - Visibility setting
  • metadata - Project metadata (JSON)
  • created_at - Creation timestamp
  • updated_at - Last update timestamp

controlplane_team

Look up an existing team:
data "controlplane_team" "devops" {
  id = "team-xxxxx"
}

# Create agent in existing team
resource "controlplane_agent" "new_devops_agent" {
  name        = "new-devops-agent"
  description = "New agent for ${data.controlplane_team.devops.name}"
  model_id    = "kubiya/claude-sonnet-4"
  runtime     = data.controlplane_team.devops.runtime
  team_id     = data.controlplane_team.devops.id

  llm_config = jsonencode({
    temperature = 0.7
    max_tokens  = 4096
  })
}

output "team_runtime" {
  value = data.controlplane_team.devops.runtime
}
Exported Attributes:
  • id - Team ID
  • name - Team name
  • description - Team description
  • runtime - Runtime type (default or claude_code)
  • configuration - Team configuration (JSON)
  • capabilities - List of capabilities
  • created_at - Creation timestamp
  • updated_at - Last update timestamp

controlplane_agent

Look up an existing agent:
data "controlplane_agent" "existing" {
  id = "agent-xxxxx"
}

# Clone agent configuration
resource "controlplane_agent" "clone" {
  name        = "cloned-agent"
  description = "Clone of ${data.controlplane_agent.existing.name}"
  model_id    = data.controlplane_agent.existing.model_id
  runtime     = data.controlplane_agent.existing.runtime
  llm_config  = data.controlplane_agent.existing.llm_config

  # Modify some settings
  configuration = jsonencode({
    environment = "staging"
  })
}

output "agent_model" {
  value = data.controlplane_agent.existing.model_id
}

output "agent_capabilities" {
  value = data.controlplane_agent.existing.capabilities
}
Exported Attributes:
  • id - Agent ID
  • name - Agent name
  • description - Agent description
  • model_id - LLM model
  • runtime - Runtime type
  • team_id - Team ID
  • llm_config - LLM configuration (JSON)
  • configuration - Agent configuration (JSON)
  • capabilities - List of capabilities
  • status - Agent status
  • created_at - Creation timestamp
  • updated_at - Last update timestamp

controlplane_skill

Look up an existing skill:
data "controlplane_skill" "shell" {
  id = "skill-xxxxx"
}

output "skill_type" {
  value = data.controlplane_skill.shell.type
}

output "skill_config" {
  value = data.controlplane_skill.shell.configuration
}

# Reference in documentation
locals {
  available_commands = jsondecode(data.controlplane_skill.shell.configuration).allowed_commands
}

output "available_commands" {
  value = local.available_commands
}
Exported Attributes:
  • id - Skill ID
  • name - Skill name
  • description - Skill description
  • type - Skill type (shell, file_system, docker, custom)
  • enabled - Whether skill is enabled
  • configuration - Skill configuration (JSON)
  • created_at - Creation timestamp
  • updated_at - Last update timestamp

controlplane_policy

Look up an existing policy:
data "controlplane_policy" "security" {
  id = "policy-xxxxx"
}

output "policy_content" {
  value     = data.controlplane_policy.security.policy_content
  sensitive = true
}

output "policy_tags" {
  value = data.controlplane_policy.security.tags
}

# Reference policy in agent configuration
resource "controlplane_agent" "compliant_agent" {
  name     = "compliant-agent"
  model_id = "kubiya/claude-sonnet-4"
  runtime  = "claude_code"

  configuration = jsonencode({
    policies = [data.controlplane_policy.security.id]
  })
}
Exported Attributes:
  • id - Policy ID
  • name - Policy name
  • description - Policy description
  • enabled - Whether policy is enabled
  • policy_content - OPA Rego policy content
  • tags - List of tags
  • created_at - Creation timestamp
  • updated_at - Last update timestamp

controlplane_job

Look up an existing job:
data "controlplane_job" "daily_backup" {
  id = "job-xxxxx"
}

output "job_info" {
  value = {
    name         = data.controlplane_job.daily_backup.name
    trigger_type = data.controlplane_job.daily_backup.trigger_type
    cron_schedule = data.controlplane_job.daily_backup.cron_schedule
    enabled      = data.controlplane_job.daily_backup.enabled
  }
}

# Reference webhook URL
output "webhook_url" {
  value     = data.controlplane_job.daily_backup.webhook_url
  sensitive = true
}
Exported Attributes:
  • id - Job ID
  • name - Job name
  • description - Job description
  • enabled - Whether job is enabled
  • status - Job status
  • trigger_type - Trigger type (cron, webhook, manual)
  • cron_schedule - Cron expression
  • cron_timezone - Timezone for cron schedule
  • webhook_url - Full webhook URL (for webhook triggers)
  • planning_mode - Planning mode
  • entity_type - Entity type (agent, team, workflow)
  • entity_id - Entity ID
  • prompt_template - Prompt template
  • system_prompt - System prompt
  • executor_type - Executor routing type
  • worker_queue_name - Worker queue name
  • environment_name - Environment name
  • created_at - Creation timestamp
  • updated_at - Last update timestamp

controlplane_jobs

List all jobs in the control plane:
data "controlplane_jobs" "all" {}

output "all_job_names" {
  value = [for job in data.controlplane_jobs.all.jobs : job.name]
}

# Filter enabled cron jobs
locals {
  cron_jobs = [
    for job in data.controlplane_jobs.all.jobs : job
    if job.trigger_type == "cron" && job.enabled
  ]
}

output "cron_jobs" {
  value = local.cron_jobs
}

# Create agents based on existing jobs
resource "controlplane_agent" "job_monitors" {
  for_each = {
    for job in data.controlplane_jobs.all.jobs : job.id => job
    if job.enabled
  }

  name     = "monitor-${each.value.name}"
  model_id = "kubiya/claude-sonnet-4"
  runtime  = "claude_code"

  llm_config = jsonencode({
    temperature = 0.7
    max_tokens  = 4096
  })

  configuration = jsonencode({
    monitored_job_id = each.value.id
  })
}
Exported Attributes:
  • jobs - List of all jobs, each with the same attributes as controlplane_job data source

controlplane_worker_queue

Look up an existing worker queue:
data "controlplane_worker_queue" "primary" {
  id = "queue-xxxxx"
}

output "queue_info" {
  value = {
    name          = data.controlplane_worker_queue.primary.name
    status        = data.controlplane_worker_queue.primary.status
    active_workers = data.controlplane_worker_queue.primary.active_workers
    max_workers   = data.controlplane_worker_queue.primary.max_workers
  }
}

# Use in job configuration
resource "controlplane_job" "queue_specific" {
  name         = "queue-specific-job"
  trigger_type = "manual"

  planning_mode   = "predefined_agent"
  entity_type     = "agent"
  entity_id       = var.agent_id
  prompt_template = "Process task"

  executor_type     = "specific_queue"
  worker_queue_name = data.controlplane_worker_queue.primary.name
}
Exported Attributes:
  • id - Worker Queue ID
  • environment_id - Environment ID
  • name - Worker queue name
  • display_name - Display name
  • description - Queue description
  • status - Worker queue status
  • max_workers - Maximum workers allowed
  • heartbeat_interval - Seconds between heartbeats
  • tags - List of tags
  • settings - Additional settings as map
  • created_at - Creation timestamp
  • updated_at - Last update timestamp
  • active_workers - Number of active workers
  • task_queue_name - Task queue name for Temporal

controlplane_worker_queues

List all worker queues in an environment:
data "controlplane_worker_queues" "production" {
  environment_id = var.production_environment_id
}

output "production_queues" {
  value = [
    for queue in data.controlplane_worker_queues.production.queues : {
      name           = queue.name
      active_workers = queue.active_workers
      max_workers    = queue.max_workers
      status         = queue.status
    }
  ]
}

# Find queue with most capacity
locals {
  available_queues = [
    for queue in data.controlplane_worker_queues.production.queues : queue
    if queue.status == "active" && (queue.max_workers == null || queue.active_workers < queue.max_workers)
  ]

  best_queue = length(local.available_queues) > 0 ? local.available_queues[0] : null
}

# Create job on queue with capacity
resource "controlplane_job" "dynamic_routing" {
  count = local.best_queue != null ? 1 : 0

  name         = "dynamic-job"
  trigger_type = "manual"

  planning_mode   = "predefined_agent"
  entity_type     = "agent"
  entity_id       = var.agent_id
  prompt_template = "Process on available queue"

  executor_type     = "specific_queue"
  worker_queue_name = local.best_queue.name
}
Exported Attributes:
  • environment_id - Environment ID (input)
  • queues - List of worker queues, each with the same attributes as controlplane_worker_queue data source

Common Use Cases

Use Case 1: Cross-Stack References

Reference resources from a different Terraform state:
# In stack-a: Create base infrastructure
resource "controlplane_environment" "shared" {
  name = "shared-environment"
}

output "shared_env_id" {
  value = controlplane_environment.shared.id
}
# In stack-b: Reference base infrastructure
data "controlplane_environment" "shared" {
  id = var.shared_environment_id  # From stack-a output
}

resource "controlplane_agent" "app_agent" {
  name     = "app-agent"
  model_id = "kubiya/claude-sonnet-4"
  runtime  = "claude_code"

  configuration = jsonencode({
    environment = data.controlplane_environment.shared.name
  })
}

Use Case 2: Dynamic Agent Creation

Create agents based on existing team configuration:
# Look up all teams
data "controlplane_team" "devops" {
  id = var.devops_team_id
}

data "controlplane_team" "security" {
  id = var.security_team_id
}

# Create agents for each team
locals {
  teams = {
    devops   = data.controlplane_team.devops
    security = data.controlplane_team.security
  }
}

resource "controlplane_agent" "team_agents" {
  for_each = local.teams

  name     = "${each.key}-agent"
  model_id = "kubiya/claude-sonnet-4"
  runtime  = each.value.runtime
  team_id  = each.value.id

  llm_config = jsonencode({
    temperature = 0.7
    max_tokens  = 4096
  })
}

Use Case 3: Configuration Validation

Validate that resources exist before creating dependencies:
# Verify environment exists
data "controlplane_environment" "required" {
  id = var.environment_id
}

# Create resources only if environment exists
resource "controlplane_agent" "validated" {
  count = data.controlplane_environment.required.id != "" ? 1 : 0

  name     = "validated-agent"
  model_id = "kubiya/claude-sonnet-4"
  runtime  = "claude_code"

  configuration = jsonencode({
    environment = data.controlplane_environment.required.name
  })
}

Use Case 4: Import Existing Resources

Find existing resources to import into Terraform:
# Look up existing agent
data "controlplane_agent" "existing" {
  id = "agent-xxxxx"
}

# Verify it's the right agent
output "verify_agent" {
  value = {
    name    = data.controlplane_agent.existing.name
    model   = data.controlplane_agent.existing.model_id
    runtime = data.controlplane_agent.existing.runtime
  }
}

# After verification, import it
# terraform import controlplane_agent.managed agent-xxxxx

# Then manage it with Terraform
resource "controlplane_agent" "managed" {
  name     = data.controlplane_agent.existing.name
  model_id = data.controlplane_agent.existing.model_id
  runtime  = data.controlplane_agent.existing.runtime
  # ... rest of configuration
}

Use Case 5: Clone Configurations

Clone existing resources with modifications:
# Look up production agent
data "controlplane_agent" "production" {
  id = var.production_agent_id
}

# Create staging clone with same config
resource "controlplane_agent" "staging" {
  name        = "staging-${data.controlplane_agent.production.name}"
  description = "Staging clone of production agent"
  model_id    = data.controlplane_agent.production.model_id
  runtime     = data.controlplane_agent.production.runtime
  llm_config  = data.controlplane_agent.production.llm_config

  # Override environment-specific settings
  configuration = jsonencode(
    merge(
      jsondecode(data.controlplane_agent.production.configuration),
      {
        environment = "staging"
        timeout     = 300  # Shorter timeout for staging
      }
    )
  )
}

Working with JSON Attributes

Many data source attributes return JSON strings. Use jsondecode() to parse them:
data "controlplane_environment" "prod" {
  id = "env-xxxxx"
}

locals {
  env_config = jsondecode(data.controlplane_environment.prod.configuration)
  max_workers = local.env_config.max_workers
  region = local.env_config.region
}

output "environment_details" {
  value = {
    max_workers = local.max_workers
    region      = local.region
  }
}

Best Practices

1. Use Variables for IDs

Don’t hardcode resource IDs:
# Good
variable "production_env_id" {
  description = "Production environment ID"
  type        = string
}

data "controlplane_environment" "prod" {
  id = var.production_env_id
}

# Avoid
data "controlplane_environment" "prod" {
  id = "env-12345"  # Hardcoded
}

2. Validate Data Source Results

Check that required data exists:
data "controlplane_team" "required" {
  id = var.team_id
}

# Fail if team doesn't exist
resource "null_resource" "validate_team" {
  triggers = {
    team_exists = data.controlplane_team.required.id
  }

  lifecycle {
    precondition {
      condition     = data.controlplane_team.required.id != ""
      error_message = "Required team does not exist"
    }
  }
}

3. Document Data Source Usage

Add clear comments:
# Look up existing production environment
# Created manually or by base infrastructure stack
data "controlplane_environment" "production" {
  id = var.production_env_id
}

4. Use Outputs for Debugging

Output data source attributes for troubleshooting:
output "debug_environment" {
  value = {
    id     = data.controlplane_environment.prod.id
    name   = data.controlplane_environment.prod.name
    config = data.controlplane_environment.prod.configuration
  }
}

Error Handling

If a data source can’t find a resource:
Error: Cannot find resource with ID: agent-xxxxx
Solutions:
  1. Verify the resource ID is correct
  2. Check that the resource exists in your control plane
  3. Verify your API key has read permissions
  4. Ensure you’re connected to the correct control plane (hosted vs self-hosted)

Next Steps