Introduction to AI Cloud Security - Discover how to revolutionize your cloud security operations with artificial intelligence. Register now ❯

CloudQuery

Azure
Template
Tutorials

Azure Quick Wins

In this tutorial, you will find Azure resources that are likely safe to delete. This can be a significant cost savings: one CloudQuery customer saves approximately $30k/month in costs by using queries like this. Beyond cost, some of these resources are a compliance and security risk if they remain as-is.

Get started #

Install CloudQuery, point it at Postgres (local Docker or Azure PG), and use a read-only role (e.g., Reader at the subscription). Save the config as azure-safe-deletes.yaml, export POSTGRES_URL, ensure your Azure credentials are set (CLI login/service principal), and then run a single CloudQuery sync. The SQL below gives a screenshot-ready “top wins” list.

CloudQuery config and sync #

kind: source
spec:
  name: azure
  path: cloudquery/source/azure
  version: "vX.Y.Z"  # pin a recent version
  tables:
    - azure_compute_disks
    - azure_compute_virtual_machines
    - azure_network_public_ip_addresses
    - azure_network_interfaces
  # (Optional) Fetch VM runtime status for better power-state signals:
  # table_options:
  #   azure_compute_virtual_machines:
  #     VirtualMachinesOptions:
  #       - StatusOnly: "true"        # or Expand: "instanceView" if supported by your setup
  destinations: [postgres]

---
kind: destination
spec:
  name: postgres
  path: cloudquery/destination/postgresql
  version: "vA.B.C"
  spec:
    connection_string: ${POSTGRES_URL}  # e.g. postgres://user:pass@host:5432/db?sslmode=disable
Then, run your sync:
cq sync azure-safe-deletes.yaml

Useful queries #

Unattached managed disks #

-- Disks with no VM attachment (managed_by is NULL or empty).
-- If present, we also surface size via properties->>'diskSizeGB'.
SELECT
  d.subscription_id,
  d.location,
  d.name,
  d.id,
  COALESCE(NULLIF(d.managed_by, ''), '—') AS managed_by,
  NULLIF(d.properties->>'diskState', '')   AS disk_state,
  (d.properties->>'diskSizeGB')::int       AS disk_size_gb,
  d.tags
FROM azure_compute_disks d
WHERE COALESCE(NULLIF(d.managed_by, ''), '') = ''
ORDER BY disk_size_gb DESC NULLS LAST, d.location, d.name;

Unassociated Public IPs --> you pay when idle #

-- Public IPs without an ipConfiguration (not associated to a NIC/LB).
-- Azure writes the association under properties.ipConfiguration.
SELECT
  p.subscription_id,
  p.location,
  p.name,
  p.id,
  (p.properties->>'ipAddress') AS current_ip,
  p.properties->>'publicIPAllocationMethod' AS allocation_method,
  p.properties->>'ipVersion'                AS ip_version,
  p.sku,
  p.tags
FROM azure_network_public_ip_addresses p
WHERE (p.properties ? 'ipConfiguration') IS FALSE
   OR p.properties->'ipConfiguration' IS NULL
ORDER BY p.location, p.name;

Stopped/Deallocated VMs #

-- VMs whose power state looks 'stopped' or 'deallocated' (when instance_view is available).
-- This relies on runtime status being fetched (see optional table_options in YAML).
WITH power AS (
  SELECT
    vm.subscription_id,
    vm.location,
    vm.name,
    vm.id,
    -- Look inside instance_view.statuses[*].code for "PowerState/..."
    (
      SELECT s->>'code'
      FROM jsonb_path_query(vm.instance_view, '$.statuses[*] ? (@.code like_regex "PowerState/.*")') AS s
      LIMIT 1
    ) AS power_code,
    vm.tags
  FROM azure_compute_virtual_machines vm
)
SELECT
  subscription_id,
  location,
  name,
  id,
  power_code,
  tags
FROM power
WHERE power_code ~* 'PowerState/(stopped|deallocated)'
ORDER BY location, name;

Wrap-up #

CloudQuery is lightning-fast data movement that runs on your infrastructure. This is perfect for working with sensitive data (like your key Azure resources!). Get started free here to realize these cost savings. We’ve got a 14 day free trial, with no credit card required!

Related posts

Turn cloud chaos into clarity

Find out how CloudQuery can help you get clarity from a chaotic cloud environment with a personalized conversation and demo.


© 2025 CloudQuery, Inc. All rights reserved.