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
-
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.
-
Add an
importblock pointingtothe resource address you want to create, with the live resource'sid. -
Write the resource block, filling in as many fields as you know -- or skip this and run
terraform plan -generate-config-out=generated.tfto have Terraform write a starting resource block for you from the live values. -
Run
terraform plan. Terraform shows the import alongside a diff of any fields in your code that don't match the live resource. -
Update your code to match the live values shown in the diff, and re-run
terraform planuntil it shows a clean import with no changes. -
Run
terraform applyto import the resource into state. -
Remove the
importblock 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 applyExample -- 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_valuemeans 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
| Resource | Import ID |
|---|---|
devzero_cluster | Cluster ID |
devzero_workload_policy | Policy ID |
devzero_workload_policy_target | Target ID |
devzero_workload_rule | Rule ID |
devzero_node_policy | Node policy ID |
devzero_node_policy_target | Node policy target ID |
Tips
- Import parent resources first. Import a
devzero_clusterbefore adevzero_workload_policy_targetordevzero_node_policy_targetthat references its ID incluster_ids, and adevzero_workload_policy/devzero_node_policybefore the target(s) that reference itspolicy_id-- that way you can reference the parent's.idin your config instead of hardcoding the same ID twice. - Cluster tokens are not affected by import. Importing a
devzero_clusternever rotates or re-issues its token, so an already-connected in-cluster operator keeps working uninterrupted. Thetokenattribute simply comes back empty in state until the next time the resource is created from scratch. - Prefer
-generate-config-outfor large or unfamiliar resources.terraform plan -generate-config-out=generated.tfwrites 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.