Azure Cloud Discovery
Discover all Azure resources across your subscriptions, identify unmanaged infrastructure, and bring it under Terraform management.
Prerequisites
- Azure credentials configured in Settings > Cloud
- Self-hosted runner (required) — must be online and connected to your organization
- Reader role on each subscription you want to scan
Self-Hosted Runner Required
Azure cloud discovery requires a self-hosted runner running inside your Azure network. Cloud runners cannot access network-restricted resources like storage accounts with firewall rules. The runner scans your Terraform state files from Azure Blob Storage and reports results back to Controlinfra.
Set up a runner in Settings > Runners before running an Azure discovery scan.
Required Permissions
The identity used for discovery needs:
| Role | Scope | Purpose |
|---|---|---|
| Reader | Subscription(s) | Enumerate all resources via Resource Graph |
| Storage Blob Data Reader | State storage account | Scan Terraform state files |
# Assign Reader on a subscription
az role assignment create \
--assignee YOUR_PRINCIPAL_ID \
--role "Reader" \
--scope /subscriptions/YOUR_SUBSCRIPTION_ID
# Assign blob reader on state storage (optional)
az role assignment create \
--assignee YOUR_PRINCIPAL_ID \
--role "Storage Blob Data Reader" \
--scope /subscriptions/YOUR_SUB/resourceGroups/YOUR_RG/providers/Microsoft.Storage/storageAccounts/YOUR_STORAGEMulti-Subscription Access
Grant the Reader role on each subscription you want to discover. Controlinfra automatically detects all subscriptions your credential has access to — no need to configure them individually.
Running a Discovery Scan
- Go to Cloud Discovery in the sidebar
- Click Run Discovery
- Select Azure as the provider
- Configure:
- Subscriptions — select which Azure subscriptions to scan (all accessible subscriptions are shown automatically)
- Services — choose which Azure services to scan (Compute, Networking, Storage, Database, etc.)
- Locations — select Azure regions to include
- State Files (optional) — enter your Azure Storage account and container where
.tfstatefiles are stored
- Click Start Scan
INFO
The Subscriptions tab lists every subscription your Azure credential (OIDC or Service Principal) can access. If a subscription is missing, ensure the service principal has the Reader role assigned on it.
How It Works
Phase 1: Resource Discovery
Controlinfra uses Azure Resource Graph to enumerate all resources across your selected subscriptions in a single query. Resource Graph natively supports multi-subscription queries, so scanning multiple subscriptions is just as fast as scanning one.
Phase 2: Enrichment
For key resource types, Controlinfra makes additional API calls to capture detailed configuration:
| Service | Details Captured |
|---|---|
| VMs | VM size, OS, disks, NICs, zones, identity |
| AKS | Kubernetes version, node pools (sizes, scaling), network profile |
| Networking | NSG rules, public IP allocation, load balancer config |
| Storage | Account tier, replication, encryption, network rules |
| SQL/PostgreSQL | SKU, capacity, HA, backup settings |
| Key Vault | SKU, RBAC, soft delete, network ACLs |
Phase 3: State Matching
If you provided a state file location, Controlinfra downloads and parses .tfstate files from Azure Blob Storage. Each discovered resource is matched against the state index using Azure resource IDs.
Phase 4: Cost Estimation
Orphaned (unmanaged) resources are priced using the Azure Retail Prices API with static fallbacks for common VM sizes.
Phase 5: AI Analysis
Claude AI analyzes orphaned resources to identify:
- Cleanup priorities and cost savings
- Security risks (exposed resources, missing encryption)
- Patterns (e.g., abandoned resource groups, orphaned disks)
Supported Resource Types
Controlinfra maps 100+ Azure resource types to their Terraform equivalents:
| Category | Resource Types |
|---|---|
| Compute | VMs, Scale Sets, Disks, Images, Snapshots |
| Containers | AKS, ACR, Container Instances |
| Networking | VNets, Subnets, NSGs, Public IPs, Load Balancers, App Gateways, Front Door, Firewall, Bastion, VPN Gateway, ExpressRoute |
| Storage | Storage Accounts |
| Database | SQL Server/DB, PostgreSQL Flexible, MySQL Flexible, CosmosDB, Redis Cache |
| App Service | Service Plans, Web Apps, Static Web Apps, Function Apps |
| Security | Key Vault (secrets, keys, certificates) |
| Identity | Managed Identities, Role Assignments |
| Monitoring | Log Analytics, Application Insights, Alerts |
| Messaging | Service Bus, Event Hub |
| Analytics | Databricks, Synapse, Data Factory |
| AI | Cognitive Services, ML Workspaces |
| Integration | Logic Apps, API Management, SignalR |
Terraform Code Generation
For orphaned resources, Controlinfra generates:
- HCL resource blocks with actual configuration from the discovered resource
terraform importcommands using the Azure resource ID- Provider configuration with
azurermbackend pointing to your state storage
Troubleshooting
"Azure credentials not configured"
Add Azure credentials in Settings > Cloud > Add Account with the OIDC or Service Principal auth method.
"AccessDenied" or "Reader role required"
The identity needs Reader on each subscription you want to scan. Assign via Azure CLI:
az role assignment create --assignee YOUR_ID --role Reader --scope /subscriptions/YOUR_SUBSubscription not showing in the dropdown
The Subscriptions dropdown only shows subscriptions where your credential has at least Reader access. To add a subscription:
- Go to the Azure Portal > Subscriptions > select the target subscription
- Click Access control (IAM) > Add role assignment
- Assign the Reader role to your service principal or app registration
- Wait ~1 minute for propagation, then re-open the scan modal
OIDC issuer mismatch (AADSTS700211)
If you see No matching federated identity record found for presented assertion issuer, the OIDC issuer URL in your server environment doesn't match the issuer configured in your Azure federated credential. Ensure OIDC_ISSUER matches what you set in az ad app federated-credential create.
Empty state file results
Ensure the storage account name and container are correct, and the identity has Storage Blob Data Reader on the storage account.
Missing resource types
Not all Azure resource types have Terraform equivalents. Resources without a mapping are still discovered but shown as "unknown" type.