Skip to content

Commit eff6dc6

Browse files
author
Billy York
committed
updated zero to hero
1 parent 66f0ac1 commit eff6dc6

File tree

1 file changed

+135
-31
lines changed

1 file changed

+135
-31
lines changed

ZeroToHero.MD

Lines changed: 135 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ There are several tables containing various data in Resource Graph.
3838
You do not have to specify a table. If you don't specify a table, it defaults to the resources table.
3939

4040
This query:
41-
41+
```` kusto
4242
where type =~ 'microsoft.compute/virtualmachines'
43-
43+
````
4444
and this query
45-
45+
```` kusto
4646
resources
47-
| where type =~ 'micrososft.compute/virtualmachines'
48-
47+
| where type =~ 'microsoft.compute/virtualmachines'
48+
````
4949
Are the same query and produce the same results. I would consider it a better practice to always declare a table, as there are now other tables to choose from than just the resources table.
5050
  
5151
  
@@ -68,27 +68,27 @@ Are the same query and produce the same results. I would consider it a better pr
6868
When querying the resources table, every resource has a resource type, in Azure these are your resource providers.
6969

7070
To see all available resource types from existing resources
71-
71+
```` kusto
7272
resources
7373
| distinct type
74-
74+
````
7575
As Azure services have been renamed over the years their provider names have stayed the same. For instance microsoft.compute/operationalinsights/workspaces is the provider name for Log Analytics workspaces.
7676

7777
**Notable resource type exceptions:
78-
Azure Kubernetes Services does not have a resource provider. You'll see AKS under Virtual Machine Scale Sets.
79-
8078
Azure Security Center and Azure Sentinel are Solutions installed on top of Log Analytics workspace.
79+
Unitl recently Azure Kubernetes service did not have a provider you could find in resource graph.
8180

8281
To query all virtual machines
83-
82+
```` kusto
8483
resources
8584
| where type =~ 'microsoft.compute/virtualmachines'
86-
85+
````
8786

8887
To query all Log Analytics workspaces, change the resource type to microsoft.operationalinsights/workspaces
89-
88+
```` kusto
9089
resources
9190
| where type =~ 'microsoft.operationalinsights/workspaces'
91+
````
9292
  
9393
  
9494

@@ -108,15 +108,16 @@ These are the most common operators you need when working with dynamic types.
108108
- tostring()
109109

110110
todynamic and parse_json are synonyms, meaning they perform the exact same funtion. You may see someone use one and someone else use the other, there is no right or wrong way.
111-
111+
```` kusto
112112
resources
113113
| where type =~ 'Microsoft.Network/privateEndpoints'
114114
| extend nics = parse_json(properties.networkInterfaces)
115-
115+
````
116+
```` kusto
116117
resources
117118
| where type =~ 'Microsoft.Network/privateEndpoints'
118119
| extend nics = todynamic(properties.networkInterfaces)
119-
120+
````
120121
these two queries will produce exactly the same result. Each will produce a new field called 'nics' with the resource ID of the private endpoint nic inside it.
121122

122123
Tolower and tostring are self explanatory, mv-expand expands array objects or dynamic type objects into multiple values.
@@ -128,31 +129,31 @@ Tolower and tostring are self explanatory, mv-expand expands array objects or dy
128129
The easiest properties to exract are the first layer inside a dynamic object. One of the most common things you may want to extract is the Sku of your resources.
129130

130131
For Application Services we'll use `microsoft.web/sites` as our resource type
131-
132+
```` kusto
132133
resources
133-
| where type =~ `'microsoft.web/sites'`
134+
| where type =~ 'microsoft.web/sites'
134135
| extend sku = properties.sku
135-
136+
````
136137
This query creates replaces the sku field, which is empty for App Services, and populates it with the data from inside the properties field.
137138

138139
We can also get the current state of our App Service from under the properties field.
139-
140+
```` kusto
140141
resources
141142
| where type =~ `'microsoft.web/sites'`
142143
| extend State = properties.state
143-
144+
````
144145
Sometimes we can go two layers deep in a dynamic object without any problems. This example will show you how to get the VM Hardware size of your Azure VMs.
145-
146+
```` kusto
146147
resources
147148
| where type =~ 'microsoft.compute/virtualmachines'
148149
| extend Size = properties.hardwareProfile.vmSize
149-
150+
````
150151
However when we start digging into dynamic types, what gets returned is also dynamic. Sometimes we'll need to convert these to strings. This is especially important when we start joining different resource types by their resource ID. Often these IDs are underneath the properties field.
151-
152+
```` kusto
152153
resources
153154
| where type =~ 'microsoft.compute/virtualmachines'
154155
| extend Size = tostring(properties.hardwareProfile.vmSize)
155-
156+
````
156157
adding tostring() around properties.hardwareProfile.vmSize will accomplish this goal.
157158
  
158159
  
@@ -162,45 +163,148 @@ Once you delve deeper into the dynamic type objects, you'll find there are certa
162163

163164
Application Gateways have a front end IP configuration. Within the properties field in Resource Graph the front end configuration is inside brackets with curly braces.
164165
We can get at the data by using addressing the first element of an array
165-
166+
```` kusto
166167
resources
167168
| where type =~ 'microsoft.network/applicationgateways'
168169
| extend frontEndConfig = properties.frontendIPConfigurations.[0].properties
169-
170+
````
170171
But this is inefficient, what if your data changes per resource type, or you just want all the objects in the array and don't know how many will be in each resource.
171172

172173
this is when we'll want to use mv-expand
173174

175+
```` kusto
174176
resources
175177
| where type =~ 'microsoft.network/applicationgateways'
176178
| mv-expand frontEndConfig = properties.frontendIPConfigurations
177-
179+
````
178180
Now, publicIpId is addressable dot notation, or another mv-expand to get at the data we want. To get the resource ID of the public IP of the Application Gateway we can add an extend on to the end of our previous query.
179181

182+
```` kusto
180183
resources
181184
| where type =~ 'microsoft.network/applicationgateways'
182185
| mv-expand frontEndConfig=properties.frontendIPConfigurations
183186
| extend publicIpId = frontEndConfig = `properties.properties.publicIPAddress.id`
184187
188+
````
185189
Continuing with Application Gateway example, underneath the same frontEndConfig is a second data property inside brackets. The http listener. To get the resource ID of the http listener we need to do a second mv-expand.
186190

191+
```` kusto
192+
187193
resources
188194
| where type =~ 'microsoft.network/applicationgateways'
189195
| mv-expand frontEndConfig=properties.frontendIPConfigurations
190196
| extend publicIp = `frontEndConfig.properties.publicIPAddress.id`
191197
| mv-expand httpListeners = frontEndConfig.properties.httpListeners
198+
````
192199

193200
and finally to get the http listener we can do another extend.
194-
201+
```` kusto
195202
resources
196203
| where type =~ 'microsoft.network/applicationgateways'
197204
| mv-expand frontEndConfig=properties.frontendIPConfigurations
198205
| extend publicIp = `frontEndConfig.properties.publicIPAddress.id`
199206
| mv-expand httpListeners = frontEndConfig.properties.httpListeners
200207
| extend httpListenerId = `httpListeners.id`
201-
202-
203-
## Practical Examples
208+
````
209+
210+
## Practical Examples with Joins
211+
These practical examples will show real world scenarios you want to use Resource Graph for. Many of them will require joins. Remember that we can only do 3 joins in Resource Graph. Additionally `join kind=leftouter` is the most common join type you'll want to use. Because you cannot assume that just becasue a VM exists that it has a OS disk, or that a disk exists it has a VM it belongs to. Using `leftouter` will allow you to join resources without eliminating resources by using inner unique joins.
212+
213+
Summarize count VMs by their VM Size.
214+
215+
```` Kusto
216+
Resources
217+
| where type == "microsoft.compute/virtualmachines"
218+
| summarize Count=count() by vmSize=tostring(properties.hardwareProfile.vmSize)
219+
````
220+
Summarize count VMs by their State
221+
```` Kusto
222+
Resources
223+
| where type == "microsoft.compute/virtualmachines"
224+
| extend vmState = tostring(properties.extended.instanceView.powerState.displayStatus)
225+
| extend vmState = iif(isempty(vmState), "VM State Unknown", (vmState)) | summarize count() by vmState
226+
````
227+
This query will join virtual machines with their OS disk and their NIC to get their private ip and publicIP resource ID.
228+
```` Kusto
229+
resources
230+
| where type == "microsoft.compute/virtualmachines"
231+
| extend osDiskId= tostring(properties.storageProfile.osDisk.managedDisk.id)
232+
| join(
233+
resources
234+
| where type =~ 'microsoft.compute/disks'
235+
| where properties !has 'Unattached'
236+
| where properties has 'osType'
237+
| project OS = tostring(properties.osType), osSku = tostring(sku.name), osDiskSizeGB = toint(properties.diskSizeGB), osDiskId=tostring(id))
238+
on osDiskId
239+
| extend nics=array_length(properties.networkProfile.networkInterfaces)
240+
| mv-expand nic=properties.networkProfile.networkInterfaces
241+
| where nics == 1 or nic.properties.primary =~ 'true' or isempty(nic)
242+
| extend vmId = id, vmName = name, vmSize=tostring(properties.hardwareProfile.vmSize), nicId = tostring(nic.id)
243+
| join kind=leftouter (
244+
resources
245+
| where type =~ 'microsoft.network/networkinterfaces'
246+
| extend ipConfigsCount=array_length(properties.ipConfigurations)
247+
| mv-expand ipconfig=properties.ipConfigurations
248+
| where ipConfigsCount == 1 or ipconfig.properties.primary =~ 'true'
249+
| project nicId = id, privateIP= tostring(ipconfig.properties.privateIPAddress), publicIpId = tostring(ipconfig.properties.publicIPAddress.id), subscriptionId)
250+
on nicId
251+
| project id, resourceGroup, OS, osSku, vmSize, privateIP, publicIpId, nicId, properties
252+
253+
````
254+
Join VMs with SQL and Availability Set
255+
```` Kusto
256+
resources
257+
| where type == "microsoft.compute/virtualmachines"
258+
| extend vmID = tolower(id)
259+
| extend osDiskId= tolower(tostring(properties.storageProfile.osDisk.managedDisk.id))
260+
| join kind=leftouter(
261+
resources
262+
| where type =~ 'microsoft.compute/disks'
263+
| where properties !has 'Unattached'
264+
| where properties has 'osType'
265+
| project timeCreated = tostring(properties.timeCreated), OS = tostring(properties.osType), osSku = tostring(sku.name), osDiskSizeGB = toint(properties.diskSizeGB), osDiskId=tolower(tostring(id))) on osDiskId
266+
| join kind=leftouter(
267+
resources
268+
| where type =~ 'microsoft.compute/availabilitysets'
269+
| extend VirtualMachines = array_length(properties.virtualMachines)
270+
| mv-expand VirtualMachine=properties.virtualMachines
271+
| extend FaultDomainCount = properties.platformFaultDomainCount
272+
| extend UpdateDomainCount = properties.platformUpdateDomainCount
273+
| extend vmID = tolower(VirtualMachine.id)
274+
| project AvailabilitySetID = id, vmID, FaultDomainCount, UpdateDomainCount ) on vmID
275+
| join kind=leftouter(
276+
resources
277+
| where type =~ 'microsoft.sqlvirtualmachine/sqlvirtualmachines'
278+
| extend SQLLicense = properties.sqlServerLicenseType
279+
| extend SQLImage = properties.sqlImageOffer
280+
| extend SQLSku = properties.sqlImageSku
281+
| extend SQLManagement = properties.sqlManagement
282+
| extend vmID = tostring(tolower(properties.virtualMachineResourceId))
283+
| project SQLId=id, SQLLicense, SQLImage, SQLSku, SQLManagement, vmID ) on vmID
284+
| project-away vmID1, vmID2, osDiskId1
285+
| extend Details = pack_all()
286+
| project vmID, SQLId, AvailabilitySetID, OS, resourceGroup, location, subscriptionId, SQLLicense, SQLImage,SQLSku, SQLManagement, FaultDomainCount, UpdateDomainCount, Details
287+
````
288+
Query to get Installed Solutions on Log Analytics
289+
```` Kusto
290+
resources
291+
| where type == "microsoft.operationsmanagement/solutions"
292+
| project Solution=plan.name, Workspace=tolower(tostring(properties.workspaceResourceId)), subscriptionId
293+
| join kind=leftouter(
294+
resources
295+
| where type =~ 'microsoft.operationalinsights/workspaces'
296+
| project Workspace=tolower(tostring(id)),subscriptionId) on Workspace
297+
| summarize Solutions = strcat_array(make_list(Solution), ",") by Workspace, subscriptionId
298+
| extend AzureSecurityCenter = iif(Solutions has 'Security','Enabled','Not Enabled')
299+
| extend AzureSecurityCenterFree = iif(Solutions has 'SecurityCenterFree','Enabled','Not Enabled')
300+
| extend AzureSentinel = iif(Solutions has "SecurityInsights",'Enabled','Not Enabled')
301+
| extend AzureMonitorVMs = iif(Solutions has "VMInsights",'Enabled','Not Enabled')
302+
| extend AzureMonitorContainers = iif(Solutions has 'ContainerInsights','Enabled','Not Enabled')
303+
| extend AzureAutomation = iif(Solutions has "AzureAutomation",'Enabled','Not Enabled')
304+
| extend ChangeTracking = iif(Solutions has 'ChangeTracking','Enabled','Not Enabled')
305+
| extend UpdateManagement = iif(Solutions has 'Updates','Enabled','Not Enabled')
306+
| extend UpdateCompliance = iif(Solutions has 'WaaSUpdateInsights','Enabled','Not Enabled')
307+
````
204308

205309

206310

0 commit comments

Comments
 (0)