AKS Query issue, Disk io per disk per node - azure-log-analytics

Facing issue while setting alert to aks pod for disk read per second per node.
**InsightsMetrics
| where Namespace == 'container.azm.ms/diskio'
| where TimeGenerated > ago(1h)
| where Name == 'reads'
| extend Tags = todynamic(Tags)
| extend HostName = tostring(Tags.hostName), Device = Tags.name
| extend NodeDisk = strcat(Device, "/", HostName)
| order by NodeDisk asc, TimeGenerated asc
| serialize
| extend PrevVal = iif(prev(NodeDisk) != NodeDisk, 0.0, prev(Val)), PrevTimeGenerated = iif(prev(NodeDisk) != NodeDisk, datetime(null), prev(TimeGenerated))
| where isnotnull(PrevTimeGenerated) and PrevTimeGenerated != TimeGenerated
| extend Rate = iif(PrevVal > Val, Val / (datetime_diff('Second', TimeGenerated, PrevTimeGenerated) * 1), iif(PrevVal == Val, 0.0, (Val - PrevVal) / (datetime_diff('Second', TimeGenerated, PrevTimeGenerated) * 1)))
| where isnotnull(Rate)
| project TimeGenerated, NodeDisk, Rate
| render timechart**
Kinldy help

Doing it this way helped me create an alert successfully:
Navigate to: Azure Portal > Kubernetes Services > {your cluster} > Logs
Paste the query in the query editor
Click on + New alert rule
Configure other details (Condition, Action etc.) and create the alert

Related

Defender KQL to show blocked Bluetooth Devices with all relevant fields

I'm trying to write a query to report on BlueToothPolicyTriggered events, that will return all the details to show when a device was blocked by policy AND the details of that device.
Our BT policy basically should allow everything but block file transfer over BT. That seems to be working as expected, but before rolling out wider, want a quick way to 'see' if any other devices are being blocked incorrectly or be able to refer to it if a user reports an issue so we can get all the details of the device blocked to add an exception etc.
However (and I'm new to kql) it seems once I filter a table using an 'ActionType' the columns available to report on are restricted, and in this case we lose details of the BT device that has been blocked
This shows all events that have triggered the policy and whether it was 'accepted' or 'blocked' but not the details of the device
search in (DeviceEvents) ActionType == "BluetoothPolicyTriggered"
| extend parsed=parse_json(AdditionalFields)
| extend Result = tostring(parsed.Accepted)
| extend BluetoothMACAddress = tostring(parsed.BluetoothMacAddress)
| extend PolicyName = tostring(parsed.PolicyName)
| extend PolicyPath = tostring(parsed.PolicyPath)
| summarize arg_max(Timestamp, *) by DeviceName, BluetoothMACAddress
| sort by Timestamp desc
| project Timestamp, DeviceName, DeviceId, Result, ActionType, BluetoothMACAddress, PolicyPath, PolicyName, ReportId
Then I have this which will show every BT connection, the device details im looking for, but not whether it was blocked or accepted
DeviceEvents
| extend parsed=parse_json(AdditionalFields)
| extend MediaClass = tostring(parsed.ClassName)
| extend MediaDeviceId = tostring(parsed.DeviceId)
| extend MediaDescription = tostring(parsed.DeviceDescription)
| extend MediaSerialNumber = tostring(parsed.SerialNumber)
| where MediaClass == "Bluetooth"
| project Timestamp, DeviceId, DeviceName, MediaClass, MediaDeviceId, MediaDescription, parsed
| order by Timestamp desc
Ive been trying to somehow join these together (despite being the same DeviceEvents table) with not much success. I don't trust the output as im seeing entries saying a device was blocked when I know it wasnt.
DeviceEvents
| where ActionType == "BluetoothPolicyTriggered"
| extend parsed=parse_json(AdditionalFields)
| extend Result = tostring(parsed.Accepted)
| extend BluetoothMACAddress = tostring(parsed.BluetoothMacAddress)
| extend PolicyName = tostring(parsed.PolicyName)
| extend PolicyPath = tostring(parsed.PolicyPath)
| project Timestamp, DeviceName, DeviceId, Result, ActionType, BluetoothMACAddress, PolicyPath, PolicyName, ReportId
| join kind =inner (DeviceEvents
| extend parsed=parse_json(AdditionalFields)
| extend MediaClass = tostring(parsed.ClassName)
| extend MediaDeviceId = tostring(parsed.DeviceId)
| extend MediaDescription = tostring(parsed.DeviceDescription)
| extend MediaSerialNumber = tostring(parsed.SerialNumber)
) on DeviceName
| where MediaClass == "Bluetooth"
| project Timestamp, DeviceName, Result, ActionType, MediaClass, MediaDeviceId, MediaDescription,BluetoothMACAddress
| sort by Timestamp desc
Am i going about this completely wrong ?

KQL :: return only tags with more than 4 records

I have created a Kusto query that allows me to return all our database park. The query only takes 10 lines of code:
Resources
| join kind=inner (
resourcecontainers
| where type == 'microsoft.resources/subscriptions'
| project subscriptionId, subscriptionName = name)
on subscriptionId
| where subscriptionName in~ ('Subscription1','Subscription2')
| where type =~ 'microsoft.sql/servers/databases'
| where name != 'master'
| project subscriptionName, resourceGroup, name, type, location,sku.tier, properties.requestedServiceObjectiveName, tags.customerCode
By contract we are supposed to give only 4 Azure SQL Database per customer but sometimes developers take a copy of them and they rename it _old or _backup and suddenly a customer can have 5 or 6 databases.
This increase the overall costs of the Cloud and I would like to have a list of all customers that have more than 4 databases.
In order to do so I can use the tag tags.customerCode which has the 3 letters identifier for each customer.
The code should work like this: if a customer is called ABC and there are 4 Azure SQL Databases with tags.customerCode ABC the query should return nothing. If there are 5 or 6 databases with tags.customerCode ABC the query should return all of them.
Not sure if Kusto can be that flexible.
Here is a possible solution.
It should be noted that Azure resource graph supports only a limited subset of KQL.
resourcecontainers
| where type == 'microsoft.resources/subscriptions'
//and name in~ ('Subscription1','Subscription2')
| project subscriptionId, subscriptionName = name
| join kind=inner
(
resources
| where type =~ 'microsoft.sql/servers/databases'
and name != 'master'
)
on subscriptionId
| project subscriptionId, subscriptionName, resourceGroup, name, type, location
,tier = sku.tier
,requestedServiceObjectiveName = properties.requestedServiceObjectiveName
,customerCode = tostring(tags.customerCode)
| summarize dbs = count(), details = make_list(pack_all()) by customerCode
| where dbs > 4
| mv-expand with_itemindex=db_seq ['details']
| project customerCode
,dbs
,db_seq = db_seq + 1
,subscriptionId = details.subscriptionId
,subscriptionName = details.subscriptionName
,resourceGroup = details.resourceGroup
,name = details.name
,type = details.type
,location = details.location
,tier = details.tier
,requestedServiceObjectiveName = details.requestedServiceObjectiveName

Force an Azure Resource Graoh to show 0 - KQL - Azure monitor

I want to create a pie chart showing the counts of open (not closed) alerts which is working. However, I want it to default to 0 in the chart when there is no alert for a particular severity
alertsmanagementresources
|extend Sev = tostring(parse_json(properties.essentials.severity)),
LastModifiedTime = todatetime(properties.essentials.lastModifiedDateTime)
| where tostring(parse_json(properties.essentials.alertState)) <> 'Closed'
| where resourceGroup =='ai-eazyfuel-eu-prd-rg'
| where Sev =='Sev0'
|where LastModifiedTime >=datetime(2022/07/26)
|summarize count() by Sev
Is this even possible because I understand there are no results to show but you know what end users are like
While it's feasible to write the KQL query:
Azure Resource Graph uses only a limited subset of KQL which makes the query syntax cumbersome.
Azure Resource Graph cannot display 0 size slice.
P.S.
Please note the removal of unnecessary transformations of properties and the use of ISO format for datetime.
resources
| take 1
| mv-expand severity = range(0,4) to typeof(string)
| project severity = strcat("Sev", severity)
| join kind=leftouter
(
alertsmanagementresources
| extend severity = tostring(properties.essentials.severity)
,lastModifiedDateTime = todatetime(properties.essentials.lastModifiedDateTime)
| where properties.essentials.alertState <> "Closed"
and resourceGroup == "ai-eazyfuel-eu-prd-rg"
and severity == "Sev0"
and lastModifiedDateTime >= datetime("2022-07-26")
| summarize count() by severity
) on severity
| project severity, count_ = coalesce(count_, 0)

Convert disk size from megabytes to gigabytes in KQL query

I have following query that helps me data from vm disk
InsightsMetrics
| where Namespace == "LogicalDisk"
| extend Tags = todynamic(Tags)
| extend Drive=tostring(todynamic(Tags)["vm.azm.ms/mountId"])
| extend DiskSize=tostring(todynamic(Tags)["vm.azm.ms/diskSizeMB"])
| summarize
Free_space_percentage = avgif(Val, Name == 'FreeSpacePercentage'),
Free_Gigabytes = avgif(Val, Name == 'FreeSpaceMB') /1024
by Computer, Drive
| join (
InsightsMetrics
| where Namespace == "LogicalDisk"
| extend Tags = todynamic(Tags)
| extend DiskSize=tostring(todynamic(Tags)["vm.azm.ms/diskSizeMB"])
| extend Drive=tostring(todynamic(Tags)["vm.azm.ms/mountId"])
) on Computer, Drive
| where DiskSize has "."
| summarize by Computer,Drive , Free_space_percentage, Free_Gigabytes, DiskSize
Issue is now that DiskSize is displayed in megabytes when all the rest are in gigabytes. I have now tried several hours to try convert it to gigas without luck. Could someone help me where and how should i do my convert in my query?
It seems your issue is not with converting MB to GB, but with structuring a query that will give you the average values as well as the disk size.
Assuming the disks' sizes are not changed during the query period, take_any() will do the trick.
InsightsMetrics
// | where TimeGenerated between(datetime(2022-04-01) .. datetime(2022-04-01 00:00:10))
| where Namespace == "LogicalDisk"
| extend Tags = todynamic(Tags)
| extend Drive = tostring(Tags["vm.azm.ms/mountId"])
| extend diskSizeGB = Tags["vm.azm.ms/diskSizeMB"]/1024.0
| summarize
avg_FreeSpacePercentage = avgif(Val, Name == 'FreeSpacePercentage')
,avg_FreeSpaceGB = avgif(Val, Name == 'FreeSpaceMB') /1024
,take_any(diskSizeGB)
by Computer, Drive
Computer
Drive
avg_FreeSpacePercentage
avg_FreeSpaceGB
diskSizeGB
DC00.na.contosohotels.com
C:
74.9538803100586
94.8232421875
126.50976181030273
DC00.na.contosohotels.com
D:
91.4168853759766
14.6240234375
15.998043060302734
SQL12.na.contosohotels.com
C:
57.7019577026367
72.998046875
126.50976181030273
SQL12.na.contosohotels.com
D:
92.02197265625
29.4443359375
31.998043060302734
SQL12.na.contosohotels.com
F:
99.9144668579102
127.7626953125
127.87304306030273
AppBE01.na.contosohotels.com
C:
73.2973098754883
92.7275390625
126.50976181030273
AppBE01.na.contosohotels.com
D:
91.3375244140625
14.611328125
15.998043060302734
Fiddle

How to detect if Tabular variable is empty in KQL

I have a dashboard populated with a number of Kusto KQL Queries.
Sometimes, my query below returns zero results (for instance if by miracle, there are no failures in the last 24 hours).
//my dashboard query
let failureResults = exceptions | where blahblahblah;
failureResults;
When there are no items that match the filters, my dashboard is filled with
'The query returned no Results'.
How could I go about checking if this variable is null and then doing a different op? For instance, if it's null, then I would just issue a print "No Failures for today, awesome!"; instead.
I have tried iff() statements and isempty(failures| distinct Outcome) and the like, but to no avail. For example, here is another one which didn't work:
failures | project column_ifexists(tostring(Outcome),"No failures where reported!")
Just thought on an improved solution based on pack_all() and the bag_unpack plugin
let p_threshold = ... ;// set value
let failureResults = datatable(exception_id:int,exception_val:int,exception_text:string)[1,100,"Hello" ,2,200,"World"];
failureResults
| where exception_val > p_threshold
| as t1
| project result = pack_all()
| union kind=outer (print msg = 'No Failures for today, awesome!' | where toscalar(t1 | take 1 | count) == 0 | project result = pack_all())
| evaluate bag_unpack(result)
let p_threshold = 0;
exception_id
exception_text
exception_val
1
Hello
100
2
World
200
let p_threshold = 300;
msg
No Failures for today, awesome!
Fiddle
Well... Kind of...
let p_threshold = ... ;// set value
let failureResults = datatable(exception_id:int,exception_val:int,exception_text:string)[1,100,"Hello" ,2,200,"World"];
failureResults
| where exception_val > p_threshold
| as t1
| union kind=outer (print msg = 'No Failures for today, awesome!' | where toscalar(t1 | take 1 | count) == 0)
| project-reorder msg
let p_threshold = 0;
msg
exception_id
exception_val
exception_text
1
100
Hello
2
200
World
let p_threshold = 300;
msg
exception_id
exception_val
exception_text
No Failures for today, awesome!
Fiddle