let q1 = view() {
AzureDiagnostics
| where Category == "AzureFirewallApplicationRule"
| where msg_s contains " to "
|parse kind = regex msg_s with Protocol @" request from " SourceIP ":" SourcePort @" to " TargetURL ":" targetPort @". Action: " Action @".\sN" Reason
};
let q2 = view() {
AzureDiagnostics
| where Category == "AzureFirewallApplicationRule"
| where msg_s !contains " to "
|parse kind = regex msg_s with Protocol @" request from " SourceIP ":" SourcePort @". Action: " Action @". Reason:" Reason
};
union withsource="temptable" q1,q2
| where SourceIP == "xx.xx.xx.xx"
| summarize count() by TargetURL, targetPort
//#| summarize count() by FQDN | order by count_ desc
| order by count_ desc
Description :
Review Azure Firewall Application Rules denied by firewall
AzureDiagnostics
| where Category == "AzureFirewallNetworkRule"
| parse msg_s with Protocol " request from " SourceIP ":" SourcePortInt:int " to " TargetIP ":" TargetPortInt:int *
| parse msg_s with * ". Action: " Action1a
| parse msg_s with * "was " Action1b " to " NatDestination
| parse msg_s with Protocol2 " request from " SourceIP2 " to " TargetIP2 ". Action:" Action2
| extend SourcePort = tostring(SourcePortInt),TargetPort = tostring(TargetPortInt)
| extend Action = case(Action1a == "", case(Action1b == "",Action2,Action1b), Action1a),Protocol = case(Protocol == "", Protocol2, Protocol),SourceIP = case(SourceIP == "", SourceIP2, SourceIP),TargetIP = case(TargetIP == "", TargetIP2, TargetIP),SourcePort = case(SourcePort == "", "N/A", SourcePort),TargetPort = case(TargetPort == "", "N/A", TargetPort),NatDestination = case(NatDestination == "", "N/A", NatDestination)
| project TimeGenerated,Protocol, SourceIP,SourcePort,TargetIP,TargetPort,Action, NatDestination
| where SourceIP == "10.1.4.25" and TargetPort == 22 and Action == "Deny"
| order by TimeGenerated desc
Description :
List Azure Firewall denied connections (Network Rule) for certain IP and port (modify the query as required)
AzureNetworkAnalytics_CL
| where SubType_s == "FlowLog"
| where SrcIP_s == "ipaddress"
| summarize by DestIP_s, DestPort_d
Description :
NSG Flowlog - Display flows based on Source IP (display destination port and IP). Replace the ip address as required.
AzureNetworkAnalytics_CL
| where SubType_s == "FlowLog"
| where DestIP_s == "ipaddress"
| summarize by SrcIP_s, DestPort_d
Description :
NSG Flowlog - Display flows based on Target IP (display source port and IP)
VMConnection
| where Computer == "computername"
| where TimeGenerated between (ago(1d) .. ago(5m))
| summarize Received = sum(BytesReceived/1024) by ProcessName
| top 3 by Received desc
Description :
Investigate top 3 incoming processes based on Bytes Recieved
VMConnection
| where Computer == "computername"
| where TimeGenerated between (ago(1d) .. ago(5m))
| summarize Sent = sum(BytesSent/1024) by ProcessName
| top 3 by Sent desc
Description :
Investigate top 3 outgoing processes based on Bytes Sent
VMConnection
| where Computer == "computername"
| where TimeGenerated between (ago(1d) .. ago(5m))
| summarize Received = sum(BytesReceived/1024), Sent = sum(BytesSent/1024) by ProcessName
| extend TotalMb = Received + Sent
| extend SentMb = Sent
| extend ReceivedMb = Received
| project ProcessName, SentMb, ReceivedMb, TotalMb
| top 3 by TotalMb desc
Description :
Investigate top 3 processes based on Bytes Received and Sent
Perf
| where Computer == "computername"
| where TimeGenerated between (ago(1d) .. ago(5m))
| where ObjectName == "Network Adapter"
| where InstanceName == "Microsoft Hyper-V Network Adapter _2"
| summarize avg(CounterValue) by CounterName, bin(TimeGenerated, 30min)
| order by TimeGenerated desc
| render timechart
Description :
Graph showing network Sent/sec and Received/sec, 30 minute average for the timeframe
VMConnection
| where Computer == "computername"
| where TimeGenerated between (ago(1d) .. ago(5m))
| extend DNS = tostring(parse_json(RemoteDnsQuestions)[0])
| summarize Received = sum(BytesReceived/1024), Sent = sum(BytesSent/1024) by DestinationIp, DNS
| extend TotalMb = Received + Sent
| extend SentMb = Sent
| extend ReceivedMb = Received
| project DestinationIp, SentMb, ReceivedMb, TotalMb, DNS
| top 3 by TotalMb desc
Description :
Investigate top 3 communication partners based on Bytes Received and Bytes Sent
VMConnection
| where TimeGenerated between (ago(1d) .. ago(5m))
| summarize Events = count() by Computer
| top 10 by Events desc
Description :
Investigate top 10 computers with most inbound and outbound communications events
VMConnection
| where TimeGenerated between (ago(1d) .. ago(5m))
| summarize Events = count() by DestinationPort
| top 10 by Events desc
Description :
Investigate top 10 ports with most communications events
VMConnection
| where TimeGenerated between (ago(1d) .. ago(5m))
| summarize Received = sum(BytesReceived/1024), Sent = sum(BytesSent/1024) by DestinationPort
| extend TotalMb = Received + Sent
| extend SentMb = Sent
| extend ReceivedMb = Received
| project DestinationPort, SentMb, ReceivedMb, TotalMb
| top 3 by TotalMb desc
Description :
Investigate top 3 communication ports based on Bytes Received and Bytes Sent
VMConnection
| where TimeGenerated between (ago(1d) .. ago(5m))
| summarize Received = sum(BytesReceived/1024), Sent = sum(BytesSent/1024) by ProcessName
| extend TotalMb = Received + Sent
| extend SentMb = Sent
| extend ReceivedMb = Received
| project ProcessName, SentMb, ReceivedMb, TotalMb
| top 3 by TotalMb desc
Description :
Investigate top 3 communication ports based on ProcessName
Heartbeat
| summarize LastHeartBeat = arg_max(TimeGenerated, *) by Computer
Description :
If the Microsoft Monitoring agent is deployed to a computer, it sends a heart beat every minute. With this query, you can see the last heart beat sent by each computer.
Heartbeat
| summarize LastHeartBeat = arg_max(TimeGenerated, *) by Computer
| where LastHeartBeat < ago(5m)
Description :
See computers that have not sent a heart beat in 5 minutes
InsightsMetrics
| where TimeGenerated > ago(4h)
| where Namespace == "LogicalDisk"
| where Name == "FreeSpacePercentage"
| extend DiskInstance = tostring(parse_json(Tags).["vm.azm.ms/mountId"])
| summarize DiskFreeSpace = avg(Val) by bin(TimeGenerated, 4h), Computer, DiskInstance
Description :
When using VM Insights, all performance counters are collected into the Insight Metrics table. This query shows the % Free space across all disks aggregated for the last 4 hours
UpdateSummary
| summarize arg_max(TimeGenerated, *) by Computer
| project Computer, LastUpdateApplied, OldestMissingSecurityUpdateInDays, WindowsUpdateSetting, SecurityUpdatesMissing, OtherUpdatesMissing, RestartPending
Description :
If using Update Management, you can use this query to show the latest status for each computer, including how many updates are missing for each computer
UpdateRunProgress
| where TimeGenerated > ago(7d)
| summarize Failed = countif(InstallationStatus == "Failed"), FailedtoStart = countif(InstallationStatus == "FailedtoStart"), InProgress = countif(InstallationStatus == "InProgress"), Succeeded = countif(InstallationStatus == "Succeeded"), NotIncluded = countif(InstallationStatus == "NotIncluded"), NotStarted = countif(InstallationStatus == "NotStarted") by UpdateRunName
Description :
Use this query to see the number of updates in each status for each update job for the last 7 days
AzureDiagnostics
| summarize by Category
Description :
In the “Database Wait Statistics” category, you can see the details of the waiting time of internal resources that occurred in the database within the specified range period. In the “Query Store Wait Statistics” category, you can see the details of the waiting time of internal resources generated in the database from the entire information stored in the query store. You can see the errors that occurred in the “Errors” category.
AzureDiagnostics
| where Category == 'DatabaseWaitStatistics'
| summarize sum(delta_wait_time_ms_d) by wait_type_s
| sort by sum_delta_wait_time_ms_d desc
| render piechart
Description :
By focusing on resources with high latency, you can discover clues to improve database performance.
AzureDiagnostics
| where Category == 'DatabaseWaitStatistics'
| summarize sum(delta_waiting_tasks_count_d) by wait_type_s
| sort by sum_delta_waiting_tasks_count_d desc
| render piechart
Description :
By focusing on resources with high wait times, you can find clues to improve database performance.
AzureDiagnostics
| where Category == 'QueryStoreWaitStatistics'
| summarize sum(total_query_wait_time_ms_d) by wait_category_s
| sort by sum_total_query_wait_time_ms_d desc
| render piechart
Description :
By focusing on resources with high latency, you can discover clues to improve database performance. By default, one month’s worth of information is stored in the query store, so you can check in a wider range.
AzureDiagnostics
| where Category == 'Errors'
| project TimeGenerated, error_number_d, Severity, state_d, Message
Description :
Understanding the errors that occur in the database will help resolve the actual failures and prevent potential failures.
AzureDiagnostics
| where Category == 'Errors'
| summarize count() by TimeGenerated
| render timechart
Description :
Understanding the number of errors that occur in the database will help resolve the actual failures and prevent potential failures.
AzureDiagnostics
| where Category == "ResourceUsageStats"
Description :
You can check the usage status of various resources of Azure SQL Managed Instance by specifying “ResourceUsageStats” as the Category operator.
AzureDiagnostics
| where Category == "ResourceUsageStats"
| project avg_cpu_percent_s, io_bytes_read_s, io_bytes_written_s
Description :
You can see the CPU usage per second in the avg_cpu_percent_s column, the number of bytes read per second in the io_bytes_read_s column, and the number of bytes written per second in the io_bytes_written column.
AzureDiagnostics
| where Category == "ResourceUsageStats"
| sort by asc
| summarize avg(todouble(avg_cpu_percent_s)), avg(todouble(io_bytes_read_s)), avg(todouble(io_bytes_written_s)) by bin(TimeGenerated, 5m)
Description :
The avg_cpu_percent_s, io_bytes_read_s, and io_bytes_written columns are string types and must be converted to numeric types to get the average. It also sorts by TimeGenerated to display in chronological order.
AzureDiagnostics
| where Category == "ResourceUsageStats"
| sort by TimeGenerated asc
| summarize avg(todouble(avg_cpu_percent_s)), avg(todouble(io_bytes_read_s)), avg(todouble(io_bytes_written_s)) by bin(TimeGenerated, 5m)
| render timechart
Description :
Use the render operator with the timechart keyword to display a line chart.
AzureDiagnostics
| where Category == 'ExecRequests'
| where StatementType_s !in ('Batch','Execute')
| summarize Session_ID=any(SessionId_s),Request_ID=any(RequestId_s),Submit_Time=max(SubmitTime_t),Start_Time=max(StartTime_t),End_Time=max(EndTime_t),Command=any(Command_s),Status=min(Status_s), Statement_Type=any(StatementType_s),Resource_class=any(ResourceClass_s) by RequestId_s
| summarize ElapsedTime_min=anyif((End_Time - Start_Time)/1m,Start_Time > ago(30d)),Session_ID=any(Session_ID), Submit_Time=any(Submit_Time) ,Start_Time=any(Start_Time), End_Time=any(End_Time),Command=any(Command),Status=any(Status),Statement_Type=any(Statement_Type),Resource_class=any(Resource_class) by Request_ID
| order by ElapsedTime_min desc
| limit 20
Description :
In the ExecRequests category of the Dedicated SQL Pool, you can see the execution time of the query. Depending on the execution time / state of the query, the information for one query may be displayed over multiple lines, so use the summarize operator to display only the information you need. The processing time is calculated from the difference between the query end time (End_Time) and the start time (Start_Time). If you need to check for a longer period, increase the value of the previous operator argument (30d (30 days) in the sample). Also, if you want to increase the number of items displayed, increase the value of the limit operator. The value in the Request_ID column will be used in step 2.
AzureDiagnostics
| where Category == 'RequestSteps'
| where RequestId_s == 'QIDnnnnnn' //Put your QueryID here
| where Status_s != 'Running'
| summarize max(StartTime_t),max(EndTime_t),max(RequestId_s),max(OperationType_s),max(RowCount_d),max(Command_s),max(Status_s) by StepIndex_d
| order by StepIndex_d asc
Description :
In the RequestSteps category of the Dedicated SQL Pool, you can get information about each step in a distributed query. You can consider what to do by looking at the steps that are taking the longest time. Specify the Request_ID value (obtained in query analysis step 1) of the query you want to analyze in the query.
AzureDiagnostics
| where Category == 'RequestSteps'
| extend elapsedTime = EndTime_t - StartTime_t
| extend elapsedTime_min = elapsedTime/1m
| where EndTime_t >= ago(30d)
| where StartTime_t >= ago(30d)
| order by RowCount_d desc
| project RequestId_s,OperationType_s, RowCount_d,elapsedTime_min , StartTime_t, EndTime_t, Status_s
| limit 20
Description :
In the RequestSteps category of the Dedicated SQL Pool, you can get information about each step in a distributed query. By checking the query step that takes the longest time, you can find the query that needs to be dealt with. If you need to check for a longer period, increase the value of the previous operator argument (30d (30 days) in the sample). Also, if you want to increase the number of items displayed, increase the value of the limit operator. The value in the Request_ID column will be used in step 4.
AzureDiagnostics
| where Category == 'RequestSteps'
| where RequestId_s == 'QIDnnnnn' //Insert your QID here
| where Status_s != 'Running'
| summarize max(StartTime_t),max(EndTime_t),max(RequestId_s),max(OperationType_s),max(RowCount_d),max(Command_s),max(Status_s) by StepIndex_d
| order by StepIndex_d asc
Description :
You can check the query processing status in the ExecRequests category of the Dedicated SQL Pool. Specify the Request_ID value (obtained in query analysis step 2) of the query you want to analyze in the query.
search *
| where TimeGenerated > ago(24h)
| distinct $table
| where $table startswith "WVD"
Description :
Diagnostic logs related to AVD are sent out to separate tables for each type. This query lists the all tables related to AVDs (logs must have sent within the last 24 hours).
For more information for diagnostic logs for AVD
https://docs.microsoft.com/ja-jp/azure/virtual-desktop/diagnostics-log-analytics
WVDAgentHealthStatus
| where TimeGenerated > ago(24h)
| summarize TotalActiveSessions=sum(toint(ActiveSessions))/10 by bin(TimeGenerated, 5m)
| render timechart
Description :
AVD Agent send various status information including the number of active sessions every 30 seconds to ‘WVDAgentHealthStatus’ table.
This query display changes in number of inactive sessions (e.g. press ‘x’ button to close the window but the session remains) aggregated by sum() function.
WVDAgentHealthStatus
| where TimeGenerated > ago(24h)
| summarize TotalInactiveSessions=sum(toint(InactiveSessions))/10 by bin(TimeGenerated, 5m)
| render timechart
Description :
Display changes in number of inactive sessions (e.g. press ‘x’ button to close the window but the session remains) aggregated by sum() function
WVDAgentHealthStatus
| where TimeGenerated > ago(10m)
| summarize arg_max(TimeGenerated,*) by SessionHostName
| project SessionHostName, ActiveSessions, InactiveSessions, TimeGenerated
Description :
List the number of active/inactive sessions from currently (at least last 10 mins) available session-hosts
WVDErrors
| where TimeGenerated > ago(24h)
| where ServiceError == "true"
Description :
List errors for ‘ServiceError’ column is set to ’true’, these records indicate you need to escalate the issue to Microsoft support
WVDErrors
| where TimeGenerated > ago(24h)
| where ServiceError == "false"
| summarize NumOfErrors=count() by ActivityType, Source, CodeSymbolic
| sort by Source
Description :
WVDErrors
| where TimeGenerated > ago(24h)
| where ServiceError == "false"
| summarize NumOfErrors=count() by UserName
Description :
WVDErrors
| where TimeGenerated > ago(24h)
| where ServiceError == "false"
| where UserName == "<User UPN>"
Description :
WVDConnections
| where TimeGenerated > ago(24h)
| extend Multi=split(_ResourceId, "/")
| extend HostPool = tostring(Multi[8])
| summarize count() by ClientSideIPAddress, HostPool
Description :
WVDConnections
| where TimeGenerated > ago(24h)
| extend Multi=split(_ResourceId, "/")
| extend HostPool = tostring(Multi[8])
| where HostPool == "<Host Pool>"
| where State == "Connected"
| project CorrelationId, UserName, StartTime=TimeGenerated
| join kind=inner(WVDConnections
| where State == "Completed"
| project EndTime = TimeGenerated, CorrelationId) on CorrelationId
| project Duration = EndTime - StartTime, UserName
| summarize ConnectionTimeTotal = sum(Duration) by UserName
Description :
The ‘WVDConnections’ table contains connect/disconnect event by users.
This query aggregates the time between connect and disconnect event from a session host and calculates the cumulative connection time for each user.
Since the ‘CorrelationId’ of the connect and disconnect events for the same session match, ‘join’ is used to combine the connect and disconnect event into a single record.
WVDConnections
| where TimeGenerated > ago(7d)
| extend Multi=split(_ResourceId, "/")
| extend HostPool = tostring(Multi[8])
| where HostPool == "<Host Pool>"
| where State == "Connected"
| project CorrelationId, UserName, StartTime = TimeGenerated
| join kind=inner(WVDConnections
| where State == "Completed"
| project EndTime = TimeGenerated, CorrelationId) on CorrelationId
| project DurationInMinutes = (EndTime - StartTime)/1m, UserName, EndTime
| summarize ConnectionTimeInMinutes=sum(DurationInMinutes) by bin(EndTime, 24h), UserName
| render timechart
Description :
This query extend the above query to provide graphical view to see transition of connection duration for each user.
In Line:11, subtracting ‘datetime’ types result in ’timespan’ type, but since this type cannot be rendered as is, convert to a scalar value in minutes by divide by ‘1m’.
WVDConnectionNetworkData
| where TimeGenerated > ago(24h)
| extend Multi = split(_ResourceId, "/")
| extend HostPool = tostring(Multi[8])
| summarize avg(EstRoundTripTimeInMs) by bin(TimeGenerated, 10m), HostPool
| render timechart
Description :
The ‘WVDConnectionNetworkData’ table contains estimated latency of session host.
This query display the latency per HostPool over the last 24 hours an every 10 minutes average.
WVDConnectionNetworkData
| where TimeGenerated > ago(24h)
| join kind=inner (WVDConnections
| distinct CorrelationId, UserName) on CorrelationId
| where UserName == "<User UPN>"
| summarize avg(EstRoundTripTimeInMs) by bin(TimeGenerated, 10m)
| render timechart
Description :
This query display the connection latency for specified user over the last 24 hours as an every 10 minutes average.
requests
| where timestamp > ago(12h)
Description :
The ‘requests’ table is a table of HTTP requests collected in Application Insights.
requests
| where timestamp > ago(12h)
| where success == false
Description :
In the ‘requests’ table, there is a row of type ‘boolean’ called ‘success’. You can aggregate failed requests by setting ‘sucess’ to ‘false’.
requests
| summarize RequestsCount=sum(itemCount), AverageDuration=avg(duration), percentiles(duration, 50, 95, 99) by operation_Name
Description :
In the ‘requests’ table, there is data that represents the time it took to respond ‘duration’. You can use ‘avg’ to aggregate averages and ‘percentiles’ to aggregate in percentiles.
requests
| summarize CountByCountry=count() by client_CountryOrRegion
| top 10 by CountByCountry
| render piechart
Description :
NA
pageViews
Description :
To aggregate pageviews, use the table in ‘pageViews’.
pageViews
| where client_type == 'Browser'
Description :
NA
pageViews
| where notempty(duration) and client_Type == 'Browser'
| extend total_duration=duration*itemCount
| summarize avg_duration=(sum(total_duration)/sum(itemCount)) by operation_Name
| top 10 by avg_duration desc
Description :
NA
let threshold = 10;
let unit = 10m;
SigninLogs
| where ResultType !in ("0", "50125", "50140")
| summarize loginfailure = count() by UserPrincipalName, bin(TimeGenerated,unit)
| where loginfailure > threshold
Description :
This query is a sample for detecting brute force attacks.
Detects sign-in failures from the SigninLogs table that exceed the number of times set in threshold
within the time set in unit
.
ResultType contains the result of the sign-in, where 0 means the sign-in was successful and there are no problems; 50125 and 50140 are events that are logged when the password is reset and when the sign-in memory is set, respectively, and are not failures, which can occur frequently. The following is an example.
SigninLogs records the sign-in of users and others to Azure AD. This logging can be enabled through Azure AD diagnostic settings and requires an Azure AD Premium P1 or P2 license. These codes are subject to change in the future, but for more information on [Azure AD Authentication and Authorization Error Codes](https://docs.microsoft.com/azure/active-directory/develop/reference-aadsts- error-codes), and you can also check the messages corresponding to the codes at https://login.microsoftonline.com/error.
let threshold = 20;
let unit = 10m;
SigninLogs
| where ResultType !in ("0", "50125", "50140")
| summarize loginfailure = dcount(UserPrincipalName) by bin(TimeGenerated,unit)
| where loginfailure > threshold
Description :
This query is a sample for detecting password spray attacks.
Brute force is an attack that attempts multiple passwords for a specific user, but since it is easy to evade and detect by locking accounts, password sprayers fix passwords to the most commonly used ones and attempt sign-in while changing users.
This query counts sign-in failures by unique users recorded within the time specified by unit
and detects those that exceed the threshold.
let threshold = 10;
let lookback = 10m;
AzureDiagnostics
| where TimeGenerated > ago(lookback)
| where Category == "ApplicationGatewayAccessLog"
| where httpStatus_d >= 300
| summarize ErrCount= count() by clientIP_s,userAgent_s
| where ErrCount > threshold
Description :
Application Gateway accesses are logged in the ApplicationGatewayAccessLog category in AzureDiagnostics.
During the reconnaissance phase, since errors are often logged with HTTP status codes, records with a status code of error (300) or higher are filtered. We find accesses that appear to be reconnaissance by detecting IP addresses that have recorded errors above a pre-defined threshold.
This query may mis-detect cases where back-end services have failed, or where multiple users from a location access the Internet through a single gateway, but they are considered to originate from the same IP address. This may cause the system to not work properly. As a mitigation measure, in addition to IP addresses, UserAgent_s is used to distinguish access sources by user agent strings, but this is not sufficient.
let threshold = 5;
let lookback = 10m;
AzureDiagnostics
| where TimeGenerated > ago(lookback)
| where Category == "ApplicationGatewayAccessLog"
| where httpStatus_d >= 300
| summarize UriCount = dcount(requestUri_s) by clientIP_s
| where UriCount > threshold
Description :
During the reconnaissance phase, accesses may be made to multiple application paths in order to brute force the paths and identify vulnerable applications. This results in multiple requestUri_s errors within a unit of time.
dcount is a function that counts the number of unique counts for a given column. In this query, the number of unique requestUri_s for clientIP_s, i.e., the number of applications that have attempted access from a particular clientIP_s, is tallied.
The record httpStatus_d is filtered for errors, so the number of applications that fail to access from a given IP can be compared to a threshold to detect attacks.
Note that in actual attacks, the source IPs may be distributed across multiple locations.
let lookback = 10m;
let threshold = 10;
let SuccessIPs = AzureDiagnostics
| where TimeGenerated > ago(lookback)
| where Category == "ApplicationGatewayAccessLog"
| where httpStatus_d between (200 .. 299)
| summarize AccessTime = max(TimeGenerated) by clientIP_s;
AzureDiagnostics
| where TimeGenerated > ago(lookback)
| where httpStatus_d >= 300
| join kind=inner SuccessIPs on clientIP_s
| where TimeGenerated < AccessTime
| summarize ErrBeforeSuccess = count() by clientIP_s
| where ErrBeforeSuccess > threshold
Description :
Although errors often occur during the reconnaissance phase, if the HTTP access success code changes after a series of errors, the application may have succeeded in gaining unauthorized access.
This query creates a dataset of the last successful access time for each IP address from the ApplicationGatewayAccessLog and joins it with a dataset of only the errors from the original ApplicationGatewayAccessLog.
Since the time of the successful access is included in the AccessTime column, TimeGenerated counts records that are smaller (older) than this and detects that the error occurred more than a threshold number of times before the successful access.
AzureDiagnostics
| where Category== "ApplicationGatewayFirewallLog"
| project TimeGenerated,clientIp_s,hostname_s,requestUri_s,ruleSetVersion_s,ruleId_s,Message,details_message_s,details_data_s
Description :
If WAF is enabled, the ApplicationGatewayFirewallLog category can record the detection results of WAF rules. This query shows the most frequently used items for WAF rule detection.
let past1month = AzureActivity
| where TimeGenerated between (ago(30d) .. startofday(now()))
| where CategoryValue == "Administrative"
| distinct OperationNameValue;
AzureActivity
| where TimeGenerated > startofday(now())
| where CategoryValue == "Administrative"
| where OperationNameValue !in (past1month)
| project TimeGenerated, OperationNameValue, OperationName, ResourceGroup, ResourceProviderValue, Caller
Description :
The Administrative table records administrative operations in Administrative
. When no system changes have occurred, administrative operations are recorded consistently, but if new, unrecorded operations are suddenly recorded, this may indicate that new change work has occurred or that a security breach has occurred.
VMProcess
| summarize UniqueProcesses = dcount(Computer) by ExecutableName
| where UniqueProcesses == 1
| join (
VMProcess
| project Computer, ExecutableName, ExecutablePath
) on ExecutableName
| distinct ExecutableName, Computer, ExecutablePath
| order by Computer, ExecutableName
Description :
Since server workloads will have the same process running on computers in the same role, if a unique process is running, it may be performing some special operation.
This could be due to some change work, or it could indicate a security breach.
SecureScores
| where TimeGenerated > ago(7d)
| extend SecureScorePercentage = PercentageScore*100
| summarize AverageScore = avg(SecureScorePercentage) by bin(TimeGenerated, 1d)
Description :
Security Score is a Posture Management feature of Microsoft Defender for Cloud that is available free of charge.
Use Azure Policy to check that Microsoft’s recommended settings are in place.
Maintain it at 90% or better, as it changes daily with the addition of workloads and the release of updates.
let backend = AzureDiagnostics
| where Category == "ApplicationGatewayAccessLog"
| where serverRouted_s <> ""
| distinct serverRouted_s;
AzureDiagnostics
| where Category == "AzureFirewallNetworkRule" or Category == "AzureFirewallApplicationRule"
| parse msg_s with * " request from " SourceIP ":" SourcePort " to " Dest ":" DestPort "." *
| project TimeGenerated,SourceIP,SourcePort,Dest,DestPort
| where SourceIP in (backend)
Description :
Communications to the back end of the Application Gateway are filtered by the Web Applicaiton Firewall, but the back end may receive undetected attack payloads.
Payloads may behave in a variety of ways, but some generate outbound data communications to communicate with C&C servers on the Internet.
This query assumes an environment where the Azure Firewall is in use and all Internet-facing communications are configured to be routed to the Azure Firewall.
Create a list of serverRouted_s since the IP addresses of the back-end servers of the pplicationGateway are recorded in serverRouted_s.
Since Azure Firewall records the source address for both IP-based network rules and URL-based application rules, the backend that attempted to send data is the one whose source address matches the list of backends created in advance.
SecurityEvent
| where EventID == 4624 and (LogonType == 2 or LogonType == 10) and AccountType == "User"
| summarize count() by Computer, Account
Description :
Generally, servers in operation do not have interactive logons except for periods of change management.
SecurityEvent is logged by Windows security logs, but can implement simple security monitoring by detecting interactive login events.
A paid SKU of Defender for Cloud is required to obtain SecurityEvents.
let maliciousIPs =
ThreatIntelligenceIndicator
| where NetworkIP <> "" and Active == true and ExpirationDateTime > now()
| distinct NetworkIP;
AzureDiagnostics
| where Category == "AzureFirewallNetworkRule"
| parse msg_s with * " request from " SourceIP ":" SourcePort " to " DestIP ":" DestPort "." *
| where DestIP in (maliciousIPs)
Description :
Azure Sentinel is required to perform this query; Azure Sentinel has data connectors to federate logs from various data sources and can pull in threat intelligence (untrusted IPs, URLs, file hashes, etc.).
In this example, the Azure Firewall logs are queried against active untrusted IP addresses obtained from threat intelligence to see if there is any suspicious external access.
ResourceContainers
| where type=='microsoft.resources/subscriptions'
| parse id with "/subscriptions/" SubscriptionID
| project subscriptionId, SubscriptionName = name
| join kind=leftouter (
Resources
| where type == 'microsoft.keyvault/vaults'
| project id, name, subscriptionId
) on subscriptionId
| join kind= leftouter (
Resources
| where type == 'microsoft.keyvault/vaults'
| summarize ResourceCount = count() by subscriptionId
) on subscriptionId
| extend RCount = iff(isnull(ResourceCount), 0, ResourceCount)
| project-away ResourceCount, subscriptionId1, subscriptionId2
| extend HealthStatus = iff(RCount == 1, "Not Compliant", "Compliant")
Description :
You can use this query in Azure Resource Graph to identify any subscriptions that do not contain KeyVaults.
resources
| where type == "microsoft.compute/virtualmachines"
| project ComputerName = name, subscriptionId
| join kind=leftouter (
resources
| where type == "microsoft.compute/virtualmachines/extensions"
| where name in ("AzureMonitorLinuxAgent", "AzureMonitorWindowsAgent")
| parse id with * "/virtualMachines/" ComputerName "/extensions/" *
| extend AMAStatus = properties.provisioningState,
WorkspaceID = tostring(properties.settings.workspaceId)
| project AMA = name, ComputerName, AMAStatus
) on ComputerName
| join kind=leftouter (
resources
| where type == "microsoft.compute/virtualmachines/extensions"
| where name in ("MicrosoftMonitoringAgent", "OmsAgentForLinux")
| parse id with * "/virtualMachines/" ComputerName "/extensions/" *
| extend MMAStatus = properties.provisioningState,
WorkspaceID = tostring(properties.settings.workspaceId)
| project MMA = name, ComputerName, MMAStatus, WorkspaceID
) on ComputerName
| join kind=leftouter (
resources
| where type == "microsoft.operationalinsights/workspaces"
| extend WorkspaceID = tostring(properties.customerId)
| project WorkspaceName = name, WorkspaceID
) on WorkspaceID
| project-away WorkspaceID1, ComputerName1, ComputerName2
| extend ["Both Agents"] = iff(isnotempty(AMA) and isnotempty(MMA), "True", "False")
Description :
You can use this query in Azure Resource Graph to identify which virtual machines have both the Azure Monitor Agent and the Microsoft Monitoring Agent deployed, and which workspace the MMA reports to.
Resources
| where type =~ 'microsoft.compute/Disks'
| where managedBy == ""
| project name, id
Description :
You can use this query in Azure Resource Graph to identify any orphaned disks.
resources
| where type == "microsoft.network/networkinterfaces"
| where isnull (properties.virtualMachine) and isnull (properties.privateEndpoint)
Description :
This query can be used in Azure Resource Graph to identify any detached NIC
resources
| where type =~ 'microsoft.network/networksecuritygroups' and isnull(properties.networkInterfaces) and isnull(properties.subnets)
| project name, resourceGroup
Description :
This query can be used in Azure Resource Graph to identify any used Network Security Group
resources
| where type == "microsoft.network/routetables" and isnull(properties.subnets)
Description :
This query can be used in Azure Resource Graph to identify any unused route table
resources
| where type == "microsoft.storage/storageaccounts" and properties.allowBlobPublicAccess == 'true'
Description :
This query can be used in Azure Resource Graph to identify any storage account with public endpoints
resources
| where type == "microsoft.storage/storageaccounts" and properties.allowBlobPublicAccess == 'true'
Description :
This query can be used in Azure Resource Graph to identify any storage accounts without an https requirement
resources
| where ['type'] == 'microsoft.network/virtualnetworks'
| mv-expand peerings=properties.virtualNetworkPeerings
| project VNetName=name, peeringName=peerings.name, peeringTarget=split(peerings.properties.remoteVirtualNetwork.id,"/",8)[0], peeringState=peerings.properties.peeringState,useRemoteGateways=peerings.properties.useRemoteGateways,
allowGatewayTransit=peerings.properties.allowGatewayTransit
Description :
Use this query to review the peering configuration of all Virtual Networks in scope
resources
| where ['type'] == 'microsoft.network/virtualnetworks'
//| project dynProperties=todynamic(properties)
| mv-expand subnetNames=properties.subnets
| project VNetName=name, Subnet=subnetNames.name,AddressPrefix=subnetNames.properties.addressPrefix,
RouteTable=split(subnetNames.properties.routeTable.id,"/",8)[0],
NSG=split(subnetNames.properties.networkSecurityGroup.id,"/",8)[0],type="microsoft.network/subnets"
Description :
This query shows details about the subnets, Addresses, NSGs, UDRs.
Resources | where type == "microsoft.compute/virtualmachines"
| summarize Total=count() by Region=location
Description :
Use this query to display a count of VMs by Azure location
resources
| where ['type'] == 'microsoft.compute/virtualmachines'
| project osType=properties.storageProfile.osDisk.osType
| summarize count() by tostring(osType)
Description :
Use this query to display a count of VMs by OS Platform
Resources
| where type == "microsoft.network/networkinterfaces"
| where isnotnull (properties.ipConfigurations[0].properties.applicationSecurityGroups)
| project subscriptionId, resourceGroup, name, ASGs=properties.ipConfigurations[0].properties.applicationSecurityGroups
| mv-expand ASGs
| project subscriptionId, resourceGroup, name, ASG=ASGs.id
| where ASG endswith "<ASG>"
Description :
NA