Installation

Install with CLI Recommended
gh skills-hub install azure-resource-lookup

Don't have the extension? Run gh extension install samueltauil/skills-hub first.

Download and extract to your repository:

.github/skills/azure-resource-lookup/

Extract the ZIP to .github/skills/ in your repo. The folder name must match azure-resource-lookup for Copilot to auto-discover it.

Skill Files (2)

SKILL.md 5.0 KB
---
name: azure-resource-lookup
description: "List, find, and show Azure resources across subscriptions or resource groups. Handles prompts like \"list websites\", \"list virtual machines\", \"list my VMs\", \"show storage accounts\", \"find container apps\", and \"what resources do I have\". USE FOR: resource inventory, find resources by tag, tag analysis, orphaned resource discovery (not for cost analysis), unattached disks, count resources by type, cross-subscription lookup, and Azure Resource Graph queries. DO NOT USE FOR: deploying/changing resources (use azure-deploy), cost optimization (use azure-cost), or non-Azure clouds."
license: MIT
metadata:
  author: Microsoft
  version: "1.0.1"
---

# Azure Resource Lookup

List, find, and discover Azure resources of any type across subscriptions and resource groups. Use Azure Resource Graph (ARG) for fast, cross-cutting queries when dedicated MCP tools don't cover the resource type.

## When to Use This Skill

Use this skill when the user wants to:
- **List resources** of any type (VMs, web apps, storage accounts, container apps, databases, etc.)
- **Show resources** in a specific subscription or resource group
- Query resources **across multiple subscriptions** or resource types
- Find **orphaned resources** (unattached disks, unused NICs, idle IPs)
- Discover resources **missing required tags** or configurations
- Get a **resource inventory** spanning multiple types
- Find resources in a **specific state** (unhealthy, failed provisioning, stopped)
- Answer "**what resources do I have?**" or "**show me my Azure resources**"

> ๐Ÿ’ก **Tip:** For single-resource-type queries, first check if a dedicated MCP tool can handle it (see routing table below). If none exists, use Azure Resource Graph.

## Quick Reference

| Property | Value |
|----------|-------|
| **Query Language** | KQL (Kusto Query Language subset) |
| **CLI Command** | `az graph query -q "<KQL>" -o table` |
| **Extension** | `az extension add --name resource-graph` |
| **MCP Tool** | `extension_cli_generate` with intent for `az graph query` |
| **Best For** | Cross-subscription queries, orphaned resources, tag audits |

## MCP Tools

| Tool | Purpose | When to Use |
|------|---------|-------------|
| `extension_cli_generate` | Generate `az graph query` commands | Primary tool โ€” generate ARG queries from user intent |
| `mcp_azure_mcp_subscription_list` | List available subscriptions | Discover subscription scope before querying |
| `mcp_azure_mcp_group_list` | List resource groups | Narrow query scope |

## Workflow

### Step 1: Check for a Dedicated MCP Tool

For single-resource-type queries, check if a dedicated MCP tool can handle it:

| Resource Type | MCP Tool | Coverage |
|---|---|---|
| Virtual Machines | `compute` | โœ… Full โ€” list, details, sizes |
| Storage Accounts | `storage` | โœ… Full โ€” accounts, blobs, tables |
| Cosmos DB | `cosmos` | โœ… Full โ€” accounts, databases, queries |
| Key Vault | `keyvault` | โš ๏ธ Partial โ€” secrets/keys only, no vault listing |
| SQL Databases | `sql` | โš ๏ธ Partial โ€” requires resource group name |
| Container Registries | `acr` | โœ… Full โ€” list registries |
| Kubernetes (AKS) | `aks` | โœ… Full โ€” clusters, node pools |
| App Service / Web Apps | `appservice` | โŒ No list command โ€” use ARG |
| Container Apps | โ€” | โŒ No MCP tool โ€” use ARG |
| Event Hubs | `eventhubs` | โœ… Full โ€” namespaces, hubs |
| Service Bus | `servicebus` | โœ… Full โ€” queues, topics |

If a dedicated tool is available with full coverage, use it. Otherwise proceed to Step 2.

### Step 2: Generate the ARG Query

Use `extension_cli_generate` to build the `az graph query` command:

```yaml
mcp_azure_mcp_extension_cli_generate
  intent: "query Azure Resource Graph to <user's request>"
  cli-type: "az"
```

See [Azure Resource Graph Query Patterns](references/azure-resource-graph.md) for common KQL patterns.

### Step 3: Execute and Format Results

Run the generated command. Use `--query` (JMESPath) to shape output:

```bash
az graph query -q "<KQL>" --query "data[].{name:name, type:type, rg:resourceGroup}" -o table
```

Use `--first N` to limit results. Use `--subscriptions` to scope.

## Error Handling

| Error | Cause | Fix |
|-------|-------|-----|
| `resource-graph extension not found` | Extension not installed | `az extension add --name resource-graph` |
| `AuthorizationFailed` | No read access to subscription | Check RBAC โ€” need Reader role |
| `BadRequest` on query | Invalid KQL syntax | Verify table/column names; use `=~` for case-insensitive type matching |
| Empty results | No matching resources or wrong scope | Check `--subscriptions` flag; verify resource type spelling |

## Constraints

- โœ… **Always** use `=~` for case-insensitive type matching (types are lowercase)
- โœ… **Always** scope queries with `--subscriptions` or `--first` for large tenants
- โœ… **Prefer** dedicated MCP tools for single-resource-type queries
- โŒ **Never** use ARG for real-time monitoring (data has slight delay)
- โŒ **Never** attempt mutations through ARG (read-only)
references/
azure-resource-graph.md 5.1 KB
# Azure Resource Graph Query Patterns

Azure Resource Graph (ARG) queries use a KQL subset against indexed Azure resource metadata. Results are near real-time across all subscriptions.

## Command Format

```bash
az graph query -q "<KQL>" --query "data[].{col1:field1, col2:field2}" -o table
```

| Flag | Purpose |
|------|---------|
| `-q` | KQL query string |
| `--query` | JMESPath to shape output columns |
| `--first N` | Limit to N results |
| `--subscriptions` | Scope to specific subscription IDs |
| `-o table` | Table output (also: json, tsv) |

## Key Tables

| Table | Contents |
|-------|----------|
| `Resources` | All ARM resources โ€” name, type, location, properties, tags, sku |
| `ResourceContainers` | Subscriptions, resource groups, management groups |
| `HealthResources` | Resource health availability status |
| `ServiceHealthResources` | Azure service health events/incidents |
| `AuthorizationResources` | Role assignments and definitions |
| `AdvisorResources` | Azure Advisor recommendations |

## KQL Essentials

- `=~` case-insensitive equals (use for `type` field โ€” types are lowercase)
- `properties.fieldName` navigates the properties JSON bag
- `mv-expand` flattens arrays (subnets, IP configs)
- `isempty()` / `isnotnull()` checks for null/empty fields
- `tostring()` converts dynamic fields for display

---

## Resource Inventory Patterns

**Count all resources by type:**
```kql
Resources | summarize count() by type | order by count_ desc
```

**Inventory by type and location:**
```kql
Resources | summarize count() by type, location | order by type asc
```

**Cross-subscription inventory with subscription names:**
```kql
Resources
| join kind=leftouter (
    ResourceContainers
    | where type == 'microsoft.resources/subscriptions'
    | project subscriptionId, subscriptionName=name
) on subscriptionId
| summarize count() by subscriptionName, type
| order by subscriptionName asc, count_ desc
```

**All resources in a resource group:**
```kql
Resources
| where resourceGroup =~ '<rg-name>'
| project name, type, location, sku.name, kind
```

## Orphaned Resource Patterns

**Unattached managed disks:**
```kql
Resources
| where type =~ 'microsoft.compute/disks'
| where isempty(managedBy)
| project name, resourceGroup, location, diskSizeGb=properties.diskSizeGB, sku=sku.name
```

**Unused public IP addresses:**
```kql
Resources
| where type =~ 'microsoft.network/publicipaddresses'
| where isempty(properties.ipConfiguration)
| project name, resourceGroup, location, sku=sku.name
```

**Orphaned network interfaces:**
```kql
Resources
| where type =~ 'microsoft.network/networkinterfaces'
| where isempty(properties.virtualMachine)
| project name, resourceGroup, location
```

**Idle load balancers (no backends):**
```kql
Resources
| where type =~ 'microsoft.network/loadbalancers'
| where array_length(properties.backendAddressPools) == 0
| project name, resourceGroup, location
```

## Tag & Compliance Patterns

**Resources missing a required tag:**
```kql
Resources
| where isnull(tags['Environment']) or isnull(tags['CostCenter'])
| project name, type, resourceGroup, tags
```

**Tag coverage analysis by type:**
```kql
Resources
| extend hasTag = isnotnull(tags['Environment'])
| summarize total=count(), tagged=countif(hasTag) by type
| extend coverage=round(100.0 * tagged / total, 1)
| order by coverage asc
```

**Resources with public network access:**
```kql
Resources
| where properties.publicNetworkAccess =~ 'Enabled'
| project name, type, resourceGroup, location
```

## Health & Diagnostics Patterns

**Resource health status:**
```kql
HealthResources
| where type =~ 'microsoft.resourcehealth/availabilitystatuses'
| where properties.availabilityState != 'Available'
| project name, state=properties.availabilityState, reason=properties.reasonType
```

**Active service health incidents:**
```kql
ServiceHealthResources
| where type =~ 'microsoft.resourcehealth/events'
| where properties.Status == 'Active'
| project name, title=properties.Title, status=properties.Status
```

**Failed provisioning states:**
```kql
Resources
| where properties.provisioningState != 'Succeeded'
| project name, type, resourceGroup, state=properties.provisioningState
```

## Service-Specific Patterns

**App Services and their plans:**
```kql
Resources
| where type =~ 'microsoft.web/sites'
| project name, kind, location, plan=properties.serverFarmId, state=properties.state, resourceGroup
```

**Container Apps:**
```kql
Resources
| where type =~ 'microsoft.app/containerapps'
| project name, location, provisioningState=properties.provisioningState, resourceGroup
```

**VNet and subnet discovery:**
```kql
Resources
| where type =~ 'microsoft.network/virtualnetworks'
| mv-expand subnet=properties.subnets
| project vnetName=name, subnetName=subnet.name, prefix=subnet.properties.addressPrefix
```

**Advisor cost recommendations:**
```kql
AdvisorResources
| where properties.category == 'Cost'
| project name, impact=properties.impact, solution=properties.shortDescription.solution
```

License (MIT)

View full license text
MIT License

Copyright 2025 (c) Microsoft Corporation.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.