{"id":27,"date":"2026-04-07T21:20:44","date_gmt":"2026-04-07T21:20:44","guid":{"rendered":"https:\/\/blog.lennardjohn.org\/?p=27"},"modified":"2026-04-07T21:22:50","modified_gmt":"2026-04-07T21:22:50","slug":"fixing-proxmox-terraform-deletes-with-curl-jq","status":"publish","type":"post","link":"https:\/\/blog.lennardjohn.org\/?p=27","title":{"rendered":"Fixing Proxmox Terraform Deletes with curl + jq"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">When I started automating my homelab using Terraform + Proxmox, everything worked great\u2026 until it didn\u2019t.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The Problem<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In a push-based deployment (via GitHub Actions), I hit a blocker:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The Proxmox Terraform provider <strong>cannot delete a running VM\/container.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That means:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Terraform tries to destroy a resource<\/li>\n\n\n\n<li>Proxmox rejects it<\/li>\n\n\n\n<li>Pipeline fails \u274c<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">So the fix is simple in theory:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u2705 Check if the VM is running \u2192 stop it \u2192 then delete it<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But Terraform doesn\u2019t handle this natively \u2014 so I built a small workaround using curl and jq.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>What You Need<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Before anything, install:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>curl (for API calls) \u2192 https:\/\/curl.se\/windows\/<\/li>\n\n\n\n<li>jq (for parsing JSON) \u2192 https:\/\/jqlang.org\/download\/<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Proxmox API Access<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You\u2019ll need a <strong>Proxmox API token.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Docs:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>https:\/\/pve.proxmox.com\/wiki\/Proxmox_VE_API#API_Tokens<\/li>\n\n\n\n<li>https:\/\/pve.proxmox.com\/pve-docs\/api-viewer\/index.html#\/nodes\/{node}\/qemu\/{vmid}\/status\/current<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Set your environment variables (Windows example):<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>setx TF_VAR_proxmox_api_url \"https:\/\/YOUR-IP:8006\/api2\/json\" setx TF_VAR_proxmox_api_token_id \"user@pam!token\" setx TF_VAR_proxmox_api_token_secret \"your-secret\"<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Quick Env Variable Ref (Windows)<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Temporary:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>set MY_VAR=HelloWorld echo %MY_VAR%<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Persistent:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>setx MY_VAR \"HelloWorld\"<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">System-wide:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>setx \/m MY_VAR \"HelloWorld\"<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The Fix (Core Idea)<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We query the VM status using the Proxmox API:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>curl -sk -H \"Authorization: PVEAPIToken=USER!TOKEN=SECRET\" \\ https:\/\/IP:8006\/api2\/json\/nodes\/NODE\/qemu\/VMID\/status\/current<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Then extract the state with jq:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>jq -r '.data.status'<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Putting It Together<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here\u2019s the logic:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>STATUS=$(curl -sk -H \"$PVE_AUTH\" \\\n\"${PVE_API}\/nodes\/${PVE_NODE}\/qemu\/${VMID}\/status\/current\" \\\n| jq -r '.data.status')\n\nif &#91; \"$STATUS\" = \"running\" ]; then\n  echo \"Stopping VM $VMID...\"\n  curl -sk -X POST -H \"$PVE_AUTH\" \\\n  \"${PVE_API}\/nodes\/${PVE_NODE}\/qemu\/${VMID}\/status\/stop\"\nfi<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now Terraform can safely destroy the VM after this runs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>When I started automating my homelab using Terraform + Proxmox, everything worked great\u2026 until it didn\u2019t. The Problem In a push-based deployment (via GitHub Actions), I hit a blocker: The Proxmox Terraform provider cannot delete a running VM\/container. That means: So the fix is simple in theory: \u2705 Check if the VM is running \u2192 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-27","post","type-post","status-publish","format-standard","hentry","category-devops"],"_links":{"self":[{"href":"https:\/\/blog.lennardjohn.org\/index.php?rest_route=\/wp\/v2\/posts\/27","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.lennardjohn.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.lennardjohn.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.lennardjohn.org\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.lennardjohn.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=27"}],"version-history":[{"count":2,"href":"https:\/\/blog.lennardjohn.org\/index.php?rest_route=\/wp\/v2\/posts\/27\/revisions"}],"predecessor-version":[{"id":37,"href":"https:\/\/blog.lennardjohn.org\/index.php?rest_route=\/wp\/v2\/posts\/27\/revisions\/37"}],"wp:attachment":[{"href":"https:\/\/blog.lennardjohn.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=27"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.lennardjohn.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=27"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.lennardjohn.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=27"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}