Ga naar inhoud

Assetqueries

Azure Resource Graph- en Log Analytics-queries om te rapporteren over een beheerd landschap van virtuele machines. Ze zijn geschreven voor de Azure Monitor-databron in Grafana: de Resource Graph-queries draaien in de modus Azure Resource Graph, de laatste draait in de modus Logs op de Log Analytics-workspace. Je kunt ze ook in de portal draaien (Resource Graph Explorer, Log Analytics) als je de regel met de dashboardvariabele ${vms} verwijdert.

Zie Assetbeheer voor de strategie die deze queries ondersteunen, en Managed VM-module voor de module.

Geldt voor de managed VM-module

De queries gebruiken de tags die de managed VM-module op de resource group van een configuratieset zet. Heeft je platform zo'n module niet, gebruik de queries dan als patroon en vervang het tagfilter door de tags van je eigen tagset (Naamgeving en tags). Test ze eerst op een kleine scope.

Hoe je een managed VM herkent

Dit bepaalt of je een rapport kunt vertrouwen, dus het is goed om het expliciet te maken. De module zet zijn platformtags onvoorwaardelijk op de resource group van een VM-configuratieset: source, vm_configuration, environment, os_type, vm_size, public_ip, shutdown_schedule_hours en één tag ownerN per owner van de configuratie. De tags die een afnemer definieert komen in plaats daarvan op de VM-resources, en zijn optioneel.

De betrouwbare definitie is dus:

Een managed VM is een virtuele machine waarvan de resource group de tags source en vm_configuration heeft die door de module zijn gezet.

Elke query hieronder begint met dat filter en joint naar de resources, daarom bevatten ze een resourcecontainers- join. De alternatieven zijn slechter:

  • Filteren op de VM-naam. De module noemt machines vm-<workload>-<configuration>-<env>-<location>-NN, maar niets houdt een team tegen om zijn eigen machines hetzelfde te noemen, en de legacy-naamgevingsvariant (useOldVMNaming) volgt het patroon niet.
  • Filteren op tags van de VM zelf. Dat zijn de tags van de afnemer en die kunnen ontbreken.
  • Filteren op de maintenance configuration. Die kan per configuratieset worden uitgezet met disable_default_maintenance_configuration, dus je mist machines.
  • Alleen vertrouwen op de dashboardvariabele ${vms}. Dat is een weergavefilter, geen scopefilter. Als de variabele wordt gevuld met een brede query, of op All staat, verschijnen niet-beheerde resources in het resultaat.

De variabele ${vms}

Vul de dashboardvariabele vanuit dezelfde definitie, zodat de variabele zelf alleen managed machines aanbiedt:

resourcecontainers
| where type =~ 'microsoft.resources/subscriptions/resourcegroups'
| where isnotempty(tags['source']) and isnotempty(tags['vm_configuration'])
| project rgId = tolower(id)
| join kind=inner (
    resources
    | where type =~ 'microsoft.compute/virtualmachines'
    | extend rgId = tolower(strcat('/subscriptions/', subscriptionId, '/resourcegroups/', resourceGroup))
    | project rgId, name
) on rgId
| distinct name
| order by name asc

In de queries hieronder beperkt de regel met ${vms} (bijvoorbeeld | where name in (${vms})) het resultaat tot de selectie in het dashboard. Verwijder die regel om te rapporteren over elke managed machine, ongeacht de selectie.

Resource Graph ondersteunt maar een deel van KQL. let en bag_keys() zijn niet beschikbaar, en een query mag hoogstens drie join- en drie mv-expand-operators bevatten. De queries hieronder blijven binnen die grenzen.

1. Inventaris van managed VM's

De actieve managed machines met hun configuratie, power state en platformtags.

resourcecontainers
| where type =~ 'microsoft.resources/subscriptions/resourcegroups'
| where isnotempty(tags['source']) and isnotempty(tags['vm_configuration'])
| project rgId = tolower(id),
    vmConfiguration = tostring(tags['vm_configuration']),
    environment = tostring(tags['environment']),
    source = tostring(tags['source']),
    owner1 = tostring(tags['owner1']),
    owner2 = tostring(tags['owner2'])
| join kind=inner (
    resources
    | where type =~ 'microsoft.compute/virtualmachines'
    | extend rgId = tolower(strcat('/subscriptions/', subscriptionId, '/resourcegroups/', resourceGroup))
    | extend
        powerState = tostring(properties.extended.instanceView.powerState.code),
        vmSize = tostring(properties.hardwareProfile.vmSize),
        osType = tostring(properties.storageProfile.osDisk.osType),
        imageOffer = tostring(properties.storageProfile.imageReference.offer),
        imageSku = tostring(properties.storageProfile.imageReference.sku)
    | project rgId, name, powerState, vmSize, osType, imageOffer, imageSku,
        resourceGroup, location, subscriptionId
) on rgId
| where name in (${vms})
| project vmname = name, powerState, vmSize, osType, imageOffer, imageSku,
    vmConfiguration, environment, source, owner1, owner2,
    resourceGroup, location, subscriptionId
| order by vmname asc

2. Alle assets van de beheerde configuratiesets

Elke resource die bij een beheerde configuratieset hoort, niet alleen de machines: disks, netwerkinterfaces, key vault, vaults, data collection rules.

resourcecontainers
| where type =~ 'microsoft.resources/subscriptions/resourcegroups'
| where isnotempty(tags['source']) and isnotempty(tags['vm_configuration'])
| project rgId = tolower(id),
    vmConfiguration = tostring(tags['vm_configuration']),
    environment = tostring(tags['environment'])
| join kind=inner (
    resources
    | extend rgId = tolower(strcat('/subscriptions/', subscriptionId, '/resourcegroups/', resourceGroup))
    | project rgId, name, type, location, resourceGroup, subscriptionId
) on rgId
| project vmConfiguration, environment, resourceType = type, name,
    resourceGroup, location, subscriptionId
| order by vmConfiguration asc, resourceType asc, name asc

Voeg | summarize assetCount = count() by vmConfiguration, environment, resourceType toe voor de aantallen per type in plaats van de afzonderlijke resources.

3. Managed VM's per owner

De module schrijft één tag ownerN per owner van de configuratie. bag_keys() is niet beschikbaar in Resource Graph, dus de owner-tags worden in een array gepakt en uitgevouwen.

resourcecontainers
| where type =~ 'microsoft.resources/subscriptions/resourcegroups'
| where isnotempty(tags['source']) and isnotempty(tags['vm_configuration'])
| extend owners = pack_array(
    tostring(tags['owner1']), tostring(tags['owner2']),
    tostring(tags['owner3']), tostring(tags['owner4']))
| mv-expand owner = owners to typeof(string)
| where isnotempty(owner)
| project rgId = tolower(id), owner, vmConfiguration = tostring(tags['vm_configuration'])
| join kind=inner (
    resources
    | where type =~ 'microsoft.compute/virtualmachines'
    | extend rgId = tolower(strcat('/subscriptions/', subscriptionId, '/resourcegroups/', resourceGroup))
    | project rgId, name, location, subscriptionId
) on rgId
| project owner, vmname = name, vmConfiguration, location, subscriptionId
| order by owner asc, vmname asc

Breid de pack_array uit als een configuratieset ooit meer dan vier owners heeft.

4. Tagdekking

Resource groups die virtuele machines bevatten maar een of meer platformtags missen. Een configuratieset die via de module is gedeployed heeft ze altijd, dus elk resultaat hier is een resource group die buiten de pipeline is aangemaakt.

resources
| where type =~ 'microsoft.compute/virtualmachines'
| extend rgId = tolower(strcat('/subscriptions/', subscriptionId, '/resourcegroups/', resourceGroup))
| summarize vmCount = count() by rgId
| join kind=inner (
    resourcecontainers
    | where type =~ 'microsoft.resources/subscriptions/resourcegroups'
    | project rgId = tolower(id), resourceGroup = name, subscriptionId, tags
) on rgId
| extend
    hasSource = isnotempty(tostring(tags['source'])),
    hasVmConfiguration = isnotempty(tostring(tags['vm_configuration'])),
    hasEnvironment = isnotempty(tostring(tags['environment'])),
    hasOsType = isnotempty(tostring(tags['os_type'])),
    hasVmSize = isnotempty(tostring(tags['vm_size'])),
    hasOwner = isnotempty(tostring(tags['owner1']))
| where not(hasSource and hasVmConfiguration and hasEnvironment and hasOsType and hasVmSize and hasOwner)
| project resourceGroup, subscriptionId, vmCount,
    hasSource, hasVmConfiguration, hasEnvironment, hasOsType, hasVmSize, hasOwner
| order by resourceGroup asc

5. Disks van de managed VM's

Opslag als onderdeel van het assetrecord: grootte, SKU en encryptie.

resourcecontainers
| where type =~ 'microsoft.resources/subscriptions/resourcegroups'
| where isnotempty(tags['source']) and isnotempty(tags['vm_configuration'])
| project rgId = tolower(id), vmConfiguration = tostring(tags['vm_configuration'])
| join kind=inner (
    resources
    | where type =~ 'microsoft.compute/disks'
    | extend rgId = tolower(strcat('/subscriptions/', subscriptionId, '/resourcegroups/', resourceGroup))
    | extend
        diskSizeGb = toint(properties.diskSizeGB),
        diskState = tostring(properties.diskState),
        skuName = tostring(sku.name),
        encryptionType = tostring(properties.encryption.type),
        attachedTo = tostring(split(tostring(properties.managedBy), '/', 8)[0])
    | project rgId, diskName = name, diskSizeGb, skuName, diskState,
        encryptionType, attachedTo, resourceGroup
) on rgId
| where isnotempty(attachedTo)
| where attachedTo in (${vms})
| project vmname = attachedTo, diskName, diskSizeGb, skuName, diskState,
    encryptionType, vmConfiguration, resourceGroup
| order by vmname asc, diskName asc

6. Status van back-upbeveiliging

Beide vaults worden gedeployed in de resource group van de configuratieset, dus hetzelfde tag- filter geldt. De Recovery Services Vault is de standaard en de Backup Vault is opt-in, dus de twee worden gecombineerd.

recoveryservicesresources
| where type =~ 'microsoft.recoveryservices/vaults/backupfabrics/protectioncontainers/protecteditems'
| extend
    vmname = tostring(properties.friendlyName),
    protectionState = tostring(properties.protectionState),
    lastBackupStatus = tostring(properties.lastBackupStatus),
    lastBackupTime = todatetime(properties.lastBackupTime),
    policyName = tostring(properties.policyName),
    vault = tostring(split(id, '/', 8)[0])
| project vmname, vault, policyName, protectionState, lastBackupStatus, lastBackupTime,
    subscriptionId, resourceGroup
| union (
    recoveryservicesresources
    | where type =~ 'microsoft.dataprotection/backupvaults/backupinstances'
    | extend
        vmname = tostring(properties.friendlyName),
        protectionState = tostring(properties.currentProtectionState),
        lastBackupStatus = tostring(properties.protectionStatus.status),
        lastBackupTime = todatetime(properties.latestRecoveryPointTime),
        policyName = tostring(properties.policyInfo.name),
        vault = tostring(split(id, '/', 8)[0])
    | project vmname, vault, policyName, protectionState, lastBackupStatus, lastBackupTime,
        subscriptionId, resourceGroup
)
| extend rgId = tolower(strcat('/subscriptions/', subscriptionId, '/resourcegroups/', resourceGroup))
| join kind=inner (
    resourcecontainers
    | where type =~ 'microsoft.resources/subscriptions/resourcegroups'
    | where isnotempty(tags['source']) and isnotempty(tags['vm_configuration'])
    | project rgId = tolower(id), vmConfiguration = tostring(tags['vm_configuration'])
) on rgId
| project vmname, vmConfiguration, vault, policyName, protectionState,
    lastBackupStatus, lastBackupTime
| order by vmname asc

7. Openstaande patches

Het patchdashboard rapporteert de laatste patch-installatie. Dit rapporteert wat er nog openstaat, de andere helft van het beeld.

patchassessmentresources
| where type =~ 'microsoft.compute/virtualmachines/patchassessmentresults'
| extend
    vmname = tostring(split(id, '/', 8)[0]),
    assessmentTime = todatetime(properties.startDateTime),
    osType = tostring(properties.osType),
    rebootPending = tostring(properties.rebootPending),
    criticalAndSecurityPatchCount = toint(properties.criticalAndSecurityPatchCount),
    otherPatchCount = toint(properties.otherPatchCount)
| extend rgId = tolower(strcat('/subscriptions/', subscriptionId, '/resourcegroups/', resourceGroup))
| join kind=inner (
    resourcecontainers
    | where type =~ 'microsoft.resources/subscriptions/resourcegroups'
    | where isnotempty(tags['source']) and isnotempty(tags['vm_configuration'])
    | project rgId = tolower(id), vmConfiguration = tostring(tags['vm_configuration'])
) on rgId
| where vmname in (${vms})
| summarize arg_max(assessmentTime, *) by vmname
| project vmname, vmConfiguration, assessmentTime, osType, rebootPending,
    criticalAndSecurityPatchCount, otherPatchCount
| order by criticalAndSecurityPatchCount desc

8. Netwerkblootstelling

Geen enkele managed VM heeft een publiek IP. Dit geeft de managed machines die er wel een hebben, dus het resultaat hoort leeg te zijn.

resourcecontainers
| where type =~ 'microsoft.resources/subscriptions/resourcegroups'
| where isnotempty(tags['source']) and isnotempty(tags['vm_configuration'])
| project rgId = tolower(id), vmConfiguration = tostring(tags['vm_configuration'])
| join kind=inner (
    resources
    | where type =~ 'microsoft.network/networkinterfaces'
    | extend rgId = tolower(strcat('/subscriptions/', subscriptionId, '/resourcegroups/', resourceGroup))
    | mv-expand ipConfig = properties.ipConfigurations
    | extend
        publicIpId = tostring(ipConfig.properties.publicIPAddress.id),
        attachedTo = tostring(split(tostring(properties.virtualMachine.id), '/', 8)[0])
    | where isnotempty(publicIpId) and isnotempty(attachedTo)
    | project rgId, nic = name, publicIpId, attachedTo, resourceGroup, subscriptionId
) on rgId
| project vmname = attachedTo, vmConfiguration, nic, publicIpId, resourceGroup, subscriptionId

9. Recente wijzigingen aan de managed VM's

Resource Graph bewaart de wijzigingsgeschiedenis. Zonder het tagfilter geeft dit wijzigingen in de hele tenant, dus de join houdt het beperkt tot het beheerde landschap.

resourcechanges
| extend
    changeTime = todatetime(properties.changeAttributes.timestamp),
    changeType = tostring(properties.changeType),
    targetId = tostring(properties.targetResourceId),
    targetType = tostring(properties.targetResourceType),
    changedBy = tostring(properties.changeAttributes.changedBy)
| where changeTime > ago(30d)
| where targetType =~ 'microsoft.compute/virtualmachines'
| extend
    resourceName = tostring(split(targetId, '/', 8)[0]),
    rgId = tolower(strcat('/subscriptions/', subscriptionId, '/resourcegroups/', resourceGroup))
| join kind=inner (
    resourcecontainers
    | where type =~ 'microsoft.resources/subscriptions/resourcegroups'
    | where isnotempty(tags['source']) and isnotempty(tags['vm_configuration'])
    | project rgId = tolower(id), vmConfiguration = tostring(tags['vm_configuration'])
) on rgId
| project changeTime, vmname = resourceName, vmConfiguration, changeType, changedBy, targetId
| order by changeTime desc

Wijzig targetType om te rapporteren over een ander resourcetype, of laat die regel weg om elke wijziging binnen de beheerde configuratiesets te zien.

10. Software-inventaris binnen de machine

Change Tracking & Inventory schrijft naar de Log Analytics Workspace. Deze query draait op de Azure Monitor-databron in de modus Logs, niet in Resource Graph.

De module deployt een workspace per VM-configuratieset, dus de workspace-scope is al het beheerde landschap en een tagfilter is niet nodig. let is hier beschikbaar, anders dan in Resource Graph.

ConfigurationData
| where ConfigDataType == "Software"
| summarize arg_max(TimeGenerated, *) by Computer, SoftwareName, CurrentVersion
| project Computer, SoftwareName, CurrentVersion, Publisher, SoftwareType, TimeGenerated
| order by Computer asc, SoftwareName asc

Vervang Software door WindowsServices, Daemons, Files of Registry voor de andere inventaristypen. ConfigurationChange bevat dezelfde data als wijzigingsgeschiedenis in plaats van als huidige status.