Skill 45 · Azure Architecture Autopilot
Subchapter 45.7
references/phase0-scanner.mdMarkdown25 KBView on GitHub
This file contains the detailed instructions for Phase 0. When the user requests analysis of existing Azure resources (Path B), read and follow this file.
Scan results are visualized as an architecture diagram, and subsequent natural-language modification requests from the user are routed to Phase 1.
Scripts
CLIAlso bundled
Gitignore🚨 Output Storage Path Rule: All outputs (scan JSON, diagram HTML, Bicep code) must be saved in a project folder under the current working directory (cwd). NEVER save them inside
~/.copilot/session-state/. The session-state directory is a temporary space and may be deleted when the session ends.
az account show 2>&1az loginaz account list --output jsonPresent the subscription list as ask_user choices. Multiple subscriptions can be selected:
ask_user({
question: "Please select the Azure subscription(s) to analyze. (You can add more one at a time for multiple selections)",
choices: [
"sub-002 (Current default subscription) (Recommended)",
"sub-001",
"Analyze all subscriptions above"
]
})ask_user({
question: "What scope of Azure resources would you like to analyze?",
choices: [
"Specify a particular resource group (Recommended)",
"Select multiple resource groups",
"All resource groups in the current subscription"
]
})rg-prod, rg-dev, rg-network)az group list → Scan all RGs (warn if there are many resources that it may take time)Combining multiple subscriptions + multiple RGs is supported:
Single subscription + single RG: Same as before (VNet boundary only) Multiple RGs (same subscription): Dashed boundary per RG Multiple subscriptions: Two-level boundary of Subscription > RG
Pass hierarchy information in the diagram JSON:
Add subscription and resourceGroup fields to the services JSON:
{
"id": "foundry",
"name": "foundry-xxx",
"type": "ai_foundry",
"subscription": "sub-002",
"resourceGroup": "rg-prod",
"details": [...]
}Pass hierarchy information via the --hierarchy parameter:
--hierarchy '[{"subscription":"sub-002","resourceGroups":["rg-prod","rg-dev"]},{"subscription":"sub-001","resourceGroups":["rg-network"]}]'Based on this information, the diagram script will:
🚨 az CLI Output Principles:
view. Direct terminal output may be truncated.--query JMESPath to extract only the required fields and reduce output size.# ✅ Correct approach — Save to file then read
az resource list -g "<RG>" --query "[].{name:name,type:type,kind:kind,location:location}" -o json | Set-Content -Path "$outDir/resources.json"
# ❌ Wrong approach — Direct terminal output (may be truncated)
az resource list -g "<RG>" -o json$outDir = "<project-name>/azure-scan"
New-Item -ItemType Directory -Path $outDir -Force | Out-Null
# Step 1: Basic resource list (name, type, kind, location)
az resource list -g "<RG>" --query "[].{name:name,type:type,kind:kind,location:location,id:id}" -o json | Set-Content "$outDir/resources.json"🚨 Immediately after reading resources.json, you MUST display the full resource list table to the user:
📋 rg-<RG> Resource List (N resources)
┌─────────────────────────┬──────────────────────────────────────────────┬─────────────────┐
│ Name │ Type │ Location │
├─────────────────────────┼──────────────────────────────────────────────┼─────────────────┤
│ my-storage │ Microsoft.Storage/storageAccounts │ koreacentral │
│ my-keyvault │ Microsoft.KeyVault/vaults │ koreacentral │
│ ... │ ... │ ... │
└─────────────────────────┴──────────────────────────────────────────────┴─────────────────┘
⏳ Retrieving detailed information...Display this table first before proceeding to detailed queries. Do not make the user wait without knowing what resources exist.
Dynamically determine detailed query commands based on the resource types found in resources.json.
Do not use a hardcoded command list. Only execute commands for types that exist in resources.json, selected from the mapping table below.
Type → Detailed Query Command Mapping:
| Type in resources.json | Detailed Query Command | Output File |
|---|---|---|
Microsoft.Network/virtualNetworks | az network vnet list -g "<RG>" --query "[].{name:name,addressSpace:addressSpace.addressPrefixes,subnets:subnets[].{name:name,prefix:addressPrefix,pePolicy:privateEndpointNetworkPolicies}}" -o json | vnets.json |
Microsoft.Network/privateEndpoints | az network private-endpoint list -g "<RG>" --query "[].{name:name,subnetId:subnet.id,targetId:privateLinkServiceConnections[0].privateLinkServiceId,groupIds:privateLinkServiceConnections[0].groupIds,state:provisioningState}" -o json | pe.json |
Microsoft.Network/networkSecurityGroups | az network nsg list -g "<RG>" --query "[].{name:name,location:location,subnets:subnets[].id,nics:networkInterfaces[].id}" -o json | nsg.json |
Microsoft.CognitiveServices/accounts | az cognitiveservices account list -g "<RG>" --query "[].{name:name,kind:kind,sku:sku.name,endpoint:properties.endpoint,publicAccess:properties.publicNetworkAccess,location:location}" -o json | cognitive.json |
Microsoft.Search/searchServices | az search service list -g "<RG>" --query "[].{name:name,sku:sku.name,publicAccess:properties.publicNetworkAccess,semanticSearch:properties.semanticSearch,location:location}" -o json 2>$null | search.json |
Microsoft.Compute/virtualMachines | az vm list -g "<RG>" --query "[].{name:name,size:hardwareProfile.vmSize,os:storageProfile.osDisk.osType,location:location,nicIds:networkProfile.networkInterfaces[].id}" -o json | vms.json |
Microsoft.Storage/storageAccounts | az storage account list -g "<RG>" --query "[].{name:name,sku:sku.name,kind:kind,hns:properties.isHnsEnabled,publicAccess:properties.publicNetworkAccess,location:location}" -o json | storage.json |
Microsoft.KeyVault/vaults | az keyvault list -g "<RG>" --query "[].{name:name,location:location}" -o json 2>$null | keyvault.json |
Microsoft.ContainerService/managedClusters | az aks list -g "<RG>" --query "[].{name:name,kubernetesVersion:kubernetesVersion,sku:sku,agentPoolProfiles:agentPoolProfiles[].{name:name,count:count,vmSize:vmSize},networkProfile:networkProfile.networkPlugin,location:location}" -o json | aks.json |
Microsoft.Web/sites | az webapp list -g "<RG>" --query "[].{name:name,kind:kind,sku:appServicePlan,state:state,defaultHostName:defaultHostName,httpsOnly:httpsOnly,location:location}" -o json | webapps.json |
Microsoft.Web/serverFarms | az appservice plan list -g "<RG>" --query "[].{name:name,sku:sku.name,tier:sku.tier,kind:kind,location:location}" -o json | appservice-plans.json |
Microsoft.DocumentDB/databaseAccounts | az cosmosdb list -g "<RG>" --query "[].{name:name,kind:kind,databaseAccountOfferType:databaseAccountOfferType,locations:locations[].locationName,publicAccess:publicNetworkAccess}" -o json | cosmosdb.json |
Microsoft.Sql/servers | az sql server list -g "<RG>" --query "[].{name:name,fullyQualifiedDomainName:fullyQualifiedDomainName,publicAccess:publicNetworkAccess,location:location}" -o json | sql-servers.json |
Microsoft.Databricks/workspaces | az databricks workspace list -g "<RG>" --query "[].{name:name,sku:sku.name,url:workspaceUrl,publicAccess:parameters.enableNoPublicIp.value,location:location}" -o json 2>$null | databricks.json |
Microsoft.Synapse/workspaces | az synapse workspace list -g "<RG>" --query "[].{name:name,sqlAdminLogin:sqlAdministratorLogin,publicAccess:publicNetworkAccess,location:location}" -o json 2>$null | synapse.json |
Microsoft.DataFactory/factories | az datafactory list -g "<RG>" --query "[].{name:name,publicAccess:publicNetworkAccess,location:location}" -o json 2>$null | adf.json |
Microsoft.EventHub/namespaces | az eventhubs namespace list -g "<RG>" --query "[].{name:name,sku:sku.name,location:location}" -o json | eventhub.json |
Microsoft.Cache/redis | az redis list -g "<RG>" --query "[].{name:name,sku:sku.name,port:port,sslPort:sslPort,publicAccess:publicNetworkAccess,location:location}" -o json | redis.json |
Microsoft.ContainerRegistry/registries | az acr list -g "<RG>" --query "[].{name:name,sku:sku.name,adminUserEnabled:adminUserEnabled,publicAccess:publicNetworkAccess,location:location}" -o json | acr.json |
Microsoft.MachineLearningServices/workspaces | az resource show --ids "<ID>" --query "{name:name,sku:sku,kind:kind,location:location,publicAccess:properties.publicNetworkAccess,hbiWorkspace:properties.hbiWorkspace,managedNetwork:properties.managedNetwork.isolationMode}" -o json | mlworkspace.json |
Microsoft.Insights/components | az monitor app-insights component show -g "<RG>" --app "<NAME>" --query "{name:name,kind:kind,instrumentationKey:instrumentationKey,workspaceResourceId:workspaceResourceId,location:location}" -o json 2>$null | appinsights-<NAME>.json |
Microsoft.OperationalInsights/workspaces | az monitor log-analytics workspace show -g "<RG>" -n "<NAME>" --query "{name:name,sku:sku.name,retentionInDays:retentionInDays,location:location}" -o json | log-analytics-<NAME>.json |
Microsoft.Network/applicationGateways | az network application-gateway list -g "<RG>" --query "[].{name:name,sku:sku,location:location}" -o json | appgateway.json |
Microsoft.Cdn/profiles / Microsoft.Network/frontDoors | az afd profile list -g "<RG>" --query "[].{name:name,sku:sku.name,location:location}" -o json 2>$null | frontdoor.json |
Microsoft.Network/azureFirewalls | az network firewall list -g "<RG>" --query "[].{name:name,sku:sku,threatIntelMode:threatIntelMode,location:location}" -o json | firewall.json |
Microsoft.Network/bastionHosts | az network bastion list -g "<RG>" --query "[].{name:name,sku:sku.name,location:location}" -o json | bastion.json |
Dynamic Query Process:
resources.jsontype fieldaz resource show --ids "<ID>" --query "{name:name,sku:sku,kind:kind,location:location,properties:properties}" -o json# Query model deployments for each Cognitive Services resource
az cognitiveservices account deployment list --name "<NAME>" -g "<RG>" --query "[].{name:name,model:properties.model.name,version:properties.model.version,sku:sku.name}" -o json | Set-Content "$outDir/<NAME>-deployments.json"az network nic list -g "<RG>" --query "[].{name:name,subnetId:ipConfigurations[0].subnet.id,privateIp:ipConfigurations[0].privateIPAddress,publicIpId:ipConfigurations[0].publicIPAddress.id}" -o json | Set-Content "$outDir/nics.json"
az network public-ip list -g "<RG>" --query "[].{name:name,ip:ipAddress,sku:sku.name}" -o json | Set-Content "$outDir/public-ips.json"From the VNet:
addressSpace.addressPrefixes → CIDRsubnets[].name, subnets[].addressPrefix → Subnet informationsubnets[].privateEndpointNetworkPolicies → PE policiesAutomatically infer relationships (connections) between scanned resources to construct the connections JSON for the diagram.
🚨 If there are insufficient connection lines, the diagram becomes meaningless. Infer as many relationships as possible.
| Relationship Type | Inference Method | connection type |
|---|---|---|
| PE → Service | Extract service ID from PE’s privateLinkServiceId | private |
| PE → VNet | Extract VNet from PE’s subnet.id | (Represented as VNet boundary) |
| Foundry → Project | Parent resource of accounts/projects | api |
| VM → NIC → Subnet | Infer VNet/Subnet from NIC’s subnet.id | (VNet boundary) |
| NSG → Subnet | Check connected subnets from NSG’s subnets[].id | network |
| NSG → NIC | Check connected VMs from NSG’s networkInterfaces[].id | network |
| NIC → Public IP | Check PIP from NIC’s publicIPAddress.id | (Included in details) |
| Databricks → VNet | Workspace’s VNet injection configuration | (VNet boundary) |
| Relationship Type | Inference Condition | connection type |
|---|---|---|
| Foundry → AI Search | Both exist in the same RG → Infer RAG connection | api (label: “RAG Search”) |
| Foundry → Storage | Both exist in the same RG → Infer data connection | data (label: “Data”) |
| AI Search → Storage | Both exist in the same RG → Infer indexing connection | data (label: “Indexing”) |
| Service → Key Vault | Key Vault exists in the same RG → Infer secret management | security (label: “Secrets”) |
| VM → Foundry/Search | VM + AI services exist in the same RG → Infer API calls | api (label: “API”) |
| DI → Foundry | Document Intelligence + Foundry exist in the same RG → Infer OCR/extraction connection | api (label: “OCR/Extract”) |
| ADF → Storage | ADF + Storage exist in the same RG → Infer data pipeline | data (label: “Pipeline”) |
| ADF → SQL | ADF + SQL exist in the same RG → Infer data source | data (label: “Source”) |
| Databricks → Storage | Both exist in the same RG → Infer data lake connection | data (label: “Data Lake”) |
Show the inferred connection list to the user and request confirmation:
> **⏳ Relationships between resources have been inferred** — Please verify if the following are correct.
Inferred connections:
- Foundry → AI Search (RAG Search)
- Foundry → Storage (Data)
- VM → Foundry (API Call)
- Document Intelligence → Foundry (OCR/Extract)
Does this look correct? Let me know if you'd like to add or remove any connections.There may be connections that cannot be inferred using the rules above. The user can freely add additional connections.
az cognitiveservices account deployment list --name "<FOUNDRY_NAME>" -g "<RG>" --query "[].{name:name,model:properties.model.name,version:properties.model.version,sku:sku.name}" -o jsonAdd each deployment’s model name, version, and SKU to the Foundry node’s details.
Convert scan results into the input format for the built-in diagram engine.
| Azure Resource Type | Diagram type |
|---|---|
Microsoft.CognitiveServices/accounts (kind: AIServices) | ai_foundry |
Microsoft.CognitiveServices/accounts (kind: OpenAI) | openai |
Microsoft.CognitiveServices/accounts (kind: FormRecognizer) | document_intelligence |
Microsoft.CognitiveServices/accounts (kind: TextAnalytics, etc.) | ai_foundry (default) |
Microsoft.CognitiveServices/accounts/projects | ai_foundry |
Microsoft.Search/searchServices | search |
Microsoft.Storage/storageAccounts | storage |
Microsoft.KeyVault/vaults | keyvault |
Microsoft.Databricks/workspaces | databricks |
Microsoft.Sql/servers | sql_server |
Microsoft.Sql/servers/databases | sql_database |
Microsoft.DocumentDB/databaseAccounts | cosmos_db |
Microsoft.Web/sites | app_service |
Microsoft.ContainerService/managedClusters | aks |
Microsoft.Web/sites (kind: functionapp) | function_app |
Microsoft.Synapse/workspaces | synapse |
Microsoft.Fabric/capacities | fabric |
Microsoft.DataFactory/factories | adf |
Microsoft.Compute/virtualMachines | vm |
Microsoft.Network/privateEndpoints | pe |
Microsoft.Network/virtualNetworks | (Represented as VNet boundary — not included in services) |
Microsoft.Network/networkSecurityGroups | nsg |
Microsoft.Network/bastionHosts | bastion |
Microsoft.OperationalInsights/workspaces | log_analytics |
Microsoft.Insights/components | app_insights |
| Other | default |
{
"id": "resource name (lowercase, special characters removed)",
"name": "actual resource name",
"type": "determined from the mapping table above",
"sku": "actual SKU (if available)",
"private": true/false, // true if a PE is connected
"details": ["property1", "property2", ...]
}Information to include in details:
If a VNet is found, display it in the boundary label via --vnet-info:
--vnet-info "10.0.0.0/16 | pe-subnet: 10.0.1.0/24 | <region>"If PEs are found, add each PE as a separate node and connect it to the corresponding service with a private type:
{"id": "pe_<serviceId>", "name": "PE: <serviceName>", "type": "pe", "details": ["groupId: <groupId>", "<status>"]}Diagram filename: <project-name>/00_arch_current.html
Use the scanned RG name as the default project name:
ask_user({
question: "Please choose a project name. (This will be the folder name for scan results)",
choices: ["<RG-name>", "azure-analysis"]
})After generating the diagram, report:
## Current Azure Architecture
[Interactive Diagram — 00_arch_current.html]
Scanned Resources (N total):
[Summary table by resource type]
What would you like to change here?
- 🔧 Performance improvement ("it's slow", "increase throughput")
- 💰 Cost optimization ("reduce costs", "make it cheaper")
- 🔒 Security hardening ("add PE", "block public access")
- 🌐 Network changes ("separate VNet", "add Bastion")
- ➕ Add/remove resources ("add a VM", "delete this")
- 📊 Monitoring ("set up logs", "add alerts")
- 🤔 Diagnostics ("is this architecture OK?", "what's wrong?")
- Or just take the diagram and stop hereWhen the user requests modifications, transition to Phase 1 (phase1-advisor.md). This is the Path B entry point, using the existing scan results as the baseline.
Ask clarifying questions to make the user’s vague requests more specific:
🔧 Performance
| User Request | Clarifying Question Example |
|---|---|
| “It’s slow” / “Response takes too long” | “Which service is slow? Should we upgrade the SKU or change the region?” |
| “I want to increase throughput” | “Which service’s throughput should we increase? Scale out? Increase DTU/RU?” |
| “AI Search indexing is slow” | “Should we add partitions? Upgrade the SKU to S2?” |
💰 Cost
| User Request | Clarifying Question Example |
|---|---|
| “I want to reduce costs” | “Which service’s cost should we reduce? SKU downgrade? Clean up unused resources?” |
| “How much does this cost?” | Look up pricing info from MS Docs and provide estimated cost based on current SKUs |
| “It’s a dev environment, so make it cheap” | “Should we switch to Free/Basic tiers? Which services?” |
🔒 Security
| User Request | Clarifying Question Example |
|---|---|
| “Harden the security” | “Should we add PEs to services that don’t have them? Check RBAC? Disable publicNetworkAccess?” |
| “Block public access” | “Should we apply PE + publicNetworkAccess: Disabled to all services?” |
| “Manage the keys” | “Should we add Key Vault and connect it with Managed Identity?” |
🌐 Network
| User Request | Clarifying Question Example |
|---|---|
| “Add PE” | “To which service? Should we add them to all services at once?” |
| “Separate the VNet” | “Which subnets should we separate? Should we also add NSGs?” |
| “Add Bastion” | “Adding Azure Bastion for VM access. Please specify the subnet CIDR.” |
➕ Add/Remove Resources
| User Request | Clarifying Question Example |
|---|---|
| “Add a VM” | “How many? What SKU? Same VNet? What OS?” |
| “Add Fabric” | “What SKU? What’s the admin email?” |
| “Delete this” | “Are you sure you want to remove [resource name]? Connected PEs will also be removed.” |
📊 Monitoring/Operations
| User Request | Clarifying Question Example |
|---|---|
| “I want to see logs” | “Should we add a Log Analytics Workspace and connect Diagnostic Settings?” |
| “Set up alerts” | “For which metrics? CPU? Error rate? Response time?” |
| “Attach Application Insights” | “To which service? App Service? Function App?” |
🔄 Migration/Changes
| User Request | Clarifying Question Example |
|---|---|
| “Change the region” | “To which region? I’ll verify that all services are available in that region.” |
| “Switch SQL to Cosmos” | “What Cosmos DB API type? (SQL/MongoDB/Cassandra) I can also provide a data migration guide.” |
| “Switch Foundry to Hub” | “Hub is suitable only when ML training/open-source models are needed. Let me verify the use case.” |
🤔 Diagnostics/Questions
| User Request | Clarifying Question Example |
|---|---|
| “What’s wrong?” | Analyze current configuration (publicNetworkAccess open, PE not connected, inappropriate SKU, etc.) and suggest improvements |
| “Is this architecture OK?” | Review against the Well-Architected Framework (security, reliability, performance, cost, operations) |
| “Is the PE connected properly?” | Check connection status with az network private-endpoint show and report |
| “Just give me the diagram” | Do not transition to Phase 1; provide the 00_arch_current.html path and finish |
Once modifications are finalized:
az resource list first to determine the resource count, then proceed with detailed queriesaz resource show⏳ Scanning resources — M of N resources completed
For resource types not in the diagram type mapping:
default type (question mark icon)This file