NewCompare CPU & GPU pricing across AWS, Azure & GCP
Terraform

Importing Resources

Bring existing DevZero resources under Terraform management without recreating them.

Importing Existing Resources

If a devzero_cluster, devzero_workload_policy, devzero_workload_policy_target, devzero_workload_rule, devzero_node_policy, or devzero_node_policy_target already exists in DevZero -- created through the console, the API, or another tool -- you can bring it under Terraform management with an import block instead of recreating it.

import blocks require Terraform 1.5 or later. On older versions, use the equivalent terraform import <resource_type>.<name> <id> CLI command instead -- it drives the same import logic.

Step-by-step

  1. Get the resource ID from the DevZero UI:

    • Cluster — Dashboard
    • Workload policy / target — Optimization → Policies → Workload
    • Node policy / target — Optimization → Policies → Node
    • Workload rule — Go to Dashboard → Clusters → [your cluster] → Workload Analysis (ensure the MPA v2 toggle is enabled), then go to the rule tab.
    • Export as IaC shortcut — On any resource row, click the ⋮ (three-dot) menu and select Export as IaC. This generates a ready-to-use resource block with all live values pre-filled — paste it directly into your code as a starting point for the next step.
  2. Add an import block pointing to the resource address you want to create, with the live resource's id.

  3. Write the resource block, filling in as many fields as you know -- or skip this and run terraform plan -generate-config-out=generated.tf to have Terraform write a starting resource block for you from the live values.

  4. Run terraform plan. Terraform shows the import alongside a diff of any fields in your code that don't match the live resource.

  5. Update your code to match the live values shown in the diff, and re-run terraform plan until it shows a clean import with no changes.

  6. Run terraform apply to import the resource into state.

  7. Remove the import block from your code -- it's only needed for this one-time step. Leaving it in is harmless (Terraform skips it once the resource is already in state), but removing it keeps your config clean.

Don't run terraform apply while terraform plan still shows changes alongside the import. That means your code doesn't fully match the live resource yet, and applying it will overwrite the live configuration to match your code -- not just adopt it as-is.

Example

import {
  to = devzero_cluster.rupam_test
  id = "22b74056-4f51-4190-bb12-de0b9a70faaf"
}

resource "devzero_cluster" "rupam_test" {
  name = "rupam-test"
}

Run terraform plan to check the resource block against the live cluster, then terraform apply to adopt it:

terraform plan
terraform apply

Example -- policy and target together

Importing a devzero_workload_policy alongside a devzero_workload_policy_target that references it. Note that the target's policy_id points at devzero_workload_policy.<name>.id rather than hardcoding the policy's ID a second time:

import {
  to = devzero_workload_policy.Aggressive_binpacking_Workload_Policy
  id = "0d6237fc-73d3-4fc1-9115-ea000559c150"
}

resource "devzero_workload_policy" "Aggressive_binpacking_Workload_Policy" {
  name = "Aggressive binpacking Workload Policy"

  cpu_vertical_scaling = {
    enabled     = true
    min_request = 20
  }

  memory_vertical_scaling = {
    enabled     = true
    min_request = 10485760
  }

  loopback_period_seconds = 604800
  action_triggers         = ["on_schedule"]
  cron_schedule           = "*/25 * * * *"
}

import {
  to = devzero_workload_policy_target.test_spark
  id = "78fbcb34-6384-4808-bd30-0e564fa25bf7"
}

resource "devzero_workload_policy_target" "test_spark" {
  name        = "test spark"
  policy_id   = devzero_workload_policy.Aggressive_binpacking_Workload_Policy.id
  enabled     = true
  priority    = 0
  cluster_ids = ["843be219-a3ad-41b8-bfa2-9ceb717aa8bc"]
  kind_filter = ["ScheduledSparkApplication"]
}

Both import blocks are independent and run in the same terraform apply -- Terraform resolves the policy_id reference from the plan, not from state, so the target doesn't need the policy to be imported first.

Example -- node policy

import {
  to = devzero_node_policy.iac_test
  id = "41a437dc-03cb-4a88-9c1c-d74265cfda3e"
}

resource "devzero_node_policy" "iac_test" {
  name            = "iac-test"
  node_class_name = "default"
  node_pool_name  = "default"
  weight          = 10

  architectures = {
    match_expressions = [
      {
        key      = "architectures"
        operator = "In"
        values   = ["arm64", "amd64"]
      },
    ]
  }

  capacity_types = {
    match_expressions = [
      {
        key      = "capacityTypes"
        operator = "In"
        values   = ["spot"]
      },
    ]
  }

  operating_systems = {
    match_expressions = [
      {
        key      = "operatingSystems"
        operator = "In"
        values   = ["linux"]
      },
    ]
  }

  disruption = {
    consolidation_policy = "WhenEmptyOrUnderutilized"
    consolidate_after    = "2h"
    expire_after         = "168h"

    budgets = [
      {
        nodes   = "100%"
        reasons = ["Empty"]
      },
      {
        nodes   = "50%"
        reasons = ["Drifted"]
      },
      {
        nodes   = "40%"
        reasons = ["Underutilized"]
      },
    ]
  }
}

Example -- workload rule

import {
  to = devzero_workload_rule.devzero_system_Deployment_devzero_zxporter_controller_manager
  id = "8d9a913c-9fd1-48ad-99df-8ae7c7f3bd0a"
}

resource "devzero_workload_rule" "devzero_system_Deployment_devzero_zxporter_controller_manager" {
  cluster_id    = "a9c849b1-6935-414d-a62e-4f3c3fb93403"
  namespace     = "devzero-system"
  kind          = "Deployment"
  name          = "devzero-zxporter-controller-manager"
  auto_generate = false

  cpu_rule = {
    enabled                 = true
    min_request             = 10
    max_request             = 32000
    limit_multiplier        = 1
    limits_removal_enabled  = true
    target_percentile       = 0.85
    max_scale_up_percent    = 1000
    max_scale_down_percent  = 1000
  }

  memory_rule = {
    enabled                   = true
    min_request               = 67108864
    max_request               = 68719476736
    limit_multiplier          = 1
    limits_adjustment_enabled = true
    target_percentile         = 1
    max_scale_up_percent     = 1000
    max_scale_down_percent   = 1000
  }

  gpu_rule = {
    enabled     = false
    min_request = 0
    max_request = 0
  }

  hpa_rule = {
    enabled                     = true
    min_replicas                = 2
    max_replicas                = 6
    max_replica_change_percent  = 0.25

    metrics = [
      {
        type                = "CPU"
        target_utilization  = "0.70"
      },
      {
        type                = "Memory"
        target_utilization  = "0.80"
      },
    ]

    behavior = {
      scale_up = {
        stabilization_window_seconds = 0
        select_policy                = "Max"
        policies = [
          { type = "Percent", value = 100, period_seconds = 60 },
          { type = "Pods", value = 4, period_seconds = 60 },
        ]
      }
      scale_down = {
        stabilization_window_seconds = 300
        select_policy                = "Min"
        policies = [
          { type = "Pods", value = 1, period_seconds = 60 },
        ]
      }
    }

    fallback = {
      replicas           = 2
      behavior           = "currentReplicas"
      failure_threshold  = 3
    }
  }

  emergency_response = {
    oom_enabled                = true
    oom_memory_multiplier      = 1.5
    oom_max_reactions          = 5
    oom_cooldown_seconds       = 10
    cpu_throttling_enabled     = true
    cpu_throttling_threshold   = 0.2
    cpu_throttling_multiplier  = 1.25
  }

  action_triggers    = ["on_detection", "on_schedule"]
  cron_schedule      = "*/25 * * * *"
  detection_triggers = ["pod_creation", "pod_update"]

  containers = [
    {
      container_name = "manager"
      cpu_rule = {
        enabled                = true
        min_request            = 10
        max_request            = 32000
        limit_multiplier       = 1
        limits_removal_enabled = true
        target_percentile      = 0.85
      }
      memory_rule = {
        enabled                   = true
        min_request               = 67108864
        max_request               = 68719476736
        limit_multiplier          = 1
        limits_adjustment_enabled = true
        target_percentile         = 1
      }
    },
  ]
}

devzero_workload_rule's import ID is the workload rule's own ID (not the cluster/namespace/kind/name path) -- get it from the DevZero console or the ListWorkloadRules API alongside the cluster ID.

Reading the diff

terraform plan prints one block per resource being imported, marked will be imported, with a ~ in front of every line whose value doesn't match your config:

# devzero_workload_policy.Aggressive_binpacking_Workload_Policy will be imported
  ~ resource "devzero_workload_policy" "Aggressive_binpacking_Workload_Policy" {
        id                      = "0d6237fc-73d3-4fc1-9115-ea000559c150"
      ~ loopback_period_seconds = 2592000 -> 604800
        name                    = "Aggressive binpacking Workload Policy"
        # (3 unchanged attributes hidden)
    }
  • ~ field = live_value -> your_value means the field exists on both sides but differs -- applying would overwrite the live value with the one in your code.
  • A field that only appears on the live side (no ->) and is missing from your resource block will be cleared by apply, since Terraform treats an absent argument as "not set."

Keep editing your resource block until terraform plan shows the import with no ~ lines -- that's a clean import.

Supported resources

ResourceImport ID
devzero_clusterCluster ID
devzero_workload_policyPolicy ID
devzero_workload_policy_targetTarget ID
devzero_workload_ruleRule ID
devzero_node_policyNode policy ID
devzero_node_policy_targetNode policy target ID

Tips

  • Import parent resources first. Import a devzero_cluster before a devzero_workload_policy_target or devzero_node_policy_target that references its ID in cluster_ids, and a devzero_workload_policy/devzero_node_policy before the target(s) that reference its policy_id -- that way you can reference the parent's .id in your config instead of hardcoding the same ID twice.
  • Cluster tokens are not affected by import. Importing a devzero_cluster never rotates or re-issues its token, so an already-connected in-cluster operator keeps working uninterrupted. The token attribute simply comes back empty in state until the next time the resource is created from scratch.
  • Prefer -generate-config-out for large or unfamiliar resources. terraform plan -generate-config-out=generated.tf writes a resource block with every live value filled in, which you can then trim down to only the fields you want Terraform to manage going forward.

On this page