Kusto Query Language (KQL) is used to write queries in Azure Data Explorer, Azure Monitor Log Analytics, Azure Sentinel, and more.
Here’s a comprehensive guide on using KQL (Kusto Query Language) to analyze SQL query audit logs in Azure Log Analytics, covering different approaches and examples:
Prerequisites
- Enable Auditing: Ensure SQL Auditing is enabled for your Azure SQL databases, sending logs to a Log Analytics workspace.
- Access Log Analytics: Navigate to the Log Analytics workspace where the audit logs are stored.
Key Tables
- AzureDiagnostics: Base table containing various logs, including SQL audit events.
- SQLSecurityAuditEvents: Dedicated table with structured audit data for easier querying.
Ways to Query SQL Audit Logs
- Direct Querying of AzureDiagnostics Table:
AzureDiagnostics
| where Category == "SQLSecurityAuditEvents"
| project TimeGenerated, DatabaseName, LoginName, Statement
| order by TimeGenerated desc
2. Querying SQLSecurityAuditEvents Table (Recommended):
SQLSecurityAuditEvents
| summarize count() by ActionName
| order by count_ desc
Here’s a comprehensive guide on using KQL (Kusto Query Language) to analyze SQL query audit logs in Azure Log Analytics, covering different approaches and examples:
Prerequisites:
- Enable Auditing: Ensure SQL Auditing is enabled for your Azure SQL databases, sending logs to a Log Analytics workspace.
- Access Log Analytics: Navigate to the Log Analytics workspace where the audit logs are stored.
Key Tables:
- AzureDiagnostics: Base table containing various logs, including SQL audit events.
- SQLSecurityAuditEvents: Dedicated table with structured audit data for easier querying.
Ways to Query SQL Audit Logs:
- Direct Querying of AzureDiagnostics Table:Code snippet
AzureDiagnostics | where Category == "SQLSecurityAuditEvents" | project TimeGenerated, DatabaseName, LoginName, Statement | order by TimeGenerated desc
Use code with caution. Learn morecontent_copy - Querying SQLSecurityAuditEvents Table (Recommended):Code snippet
SQLSecurityAuditEvents | summarize count() by ActionName | order by count_ desc
Example Queries:
- List recent queries for a specific database:
SQLSecurityAuditEvents
| where DatabaseName == "MyDatabase"
| where ActionName == "BATCH_COMPLETED_GROUP"
| project TimeGenerated, DatabaseName, LoginName, Statement
| order by TimeGenerated desc
- Find queries with long execution times:
SQLSecurityAuditEvents
| where ActionName == "BATCH_COMPLETED_GROUP"
| where DurationMs > 10000
| project TimeGenerated, DatabaseName, LoginName, Statement, DurationMs
| order by DurationMs desc
- Identify failed login attempts:
SQLSecurityAuditEvents
| where ActionName == "LOGIN_FAILED"
| project TimeGenerated, LoginName, ClientIP
Here’s a comprehensive guide on using KQL (Kusto Query Language) to analyze SQL query audit logs in Azure Log Analytics, covering different approaches and examples:
Prerequisites:
- Enable Auditing: Ensure SQL Auditing is enabled for your Azure SQL databases, sending logs to a Log Analytics workspace.
- Access Log Analytics: Navigate to the Log Analytics workspace where the audit logs are stored.
Key Tables:
- AzureDiagnostics: Base table containing various logs, including SQL audit events.
- SQLSecurityAuditEvents: Dedicated table with structured audit data for easier querying.
Ways to Query SQL Audit Logs:
- Direct Querying of AzureDiagnostics Table:Code snippet
AzureDiagnostics | where Category == "SQLSecurityAuditEvents" | project TimeGenerated, DatabaseName, LoginName, Statement | order by TimeGenerated desc
Use code with caution. Learn morecontent_copy - Querying SQLSecurityAuditEvents Table (Recommended):Code snippet
SQLSecurityAuditEvents | summarize count() by ActionName | order by count_ desc
Use code with caution. Learn morecontent_copy
Example Queries:
- List recent queries for a specific database:Code snippet
SQLSecurityAuditEvents | where DatabaseName == "MyDatabase" | where ActionName == "BATCH_COMPLETED_GROUP" | project TimeGenerated, DatabaseName, LoginName, Statement | order by TimeGenerated desc
Use code with caution. Learn morecontent_copy - Find queries with long execution times:Code snippet
SQLSecurityAuditEvents | where ActionName == "BATCH_COMPLETED_GROUP" | where DurationMs > 10000 | project TimeGenerated, DatabaseName, LoginName, Statement, DurationMs | order by DurationMs desc
Use code with caution. Learn morecontent_copy - Identify failed login attempts:Code snippet
SQLSecurityAuditEvents | where ActionName == "LOGIN_FAILED" | project TimeGenerated, LoginName, ClientIP
Use code with caution. Learn morecontent_copy
Additional Tips:
- Filter by time range: Use
where TimeGenerated >= ago(1d)
to filter for events in the last day. - Search for specific text: Use
where Statement contains "SELECT *"
to find queries containing a specific string. - Join with other tables: Enrich audit data by joining with other tables in Log Analytics (e.g.,
AzureActivityLog
). - Visualize results: Use Log Analytics charts and dashboards to create visual representations of audit data.
- Create alerts: Set up alerts to notify you of critical events or unusual activity.
SQLSecurityAuditEvents
SQLSecurityAuditEvents is a dedicated table within Azure Log Analytics that stores structured audit data related to security events and activities within Azure SQL databases. It’s specifically designed to facilitate easy analysis and monitoring of security-related actions, providing valuable insights for compliance, threat detection, and overall database security.
Key Features:
- Organized Structure: Stores audit events in a structured format, making it easy to query and analyze using KQL (Kusto Query Language).
- Detailed Information: Captures a rich set of details for each security event, including:
- Timestamp
- Database name
- Login name
- Statement executed
- Success or failure
- Client IP address
- Duration
- Action name (e.g., LOGIN_FAILED, BATCH_COMPLETED_GROUP, BACKUP_RESTORE_GROUP)
- Centralized Logging: Integrates with Azure Log Analytics, enabling centralized storage and management of audit logs across multiple databases and servers.
- Compatibility: Works with both Azure SQL Database and Azure Synapse Analytics.
Benefits:
- Enhanced Security Insights: Enables granular monitoring of database activities for security and compliance purposes.
- Threat Detection: Helps identify potential security threats like failed login attempts, unauthorized access, or suspicious queries.
- Compliance Support: Assists in meeting regulatory compliance requirements that mandate database auditing.
- Troubleshooting: Facilitates investigation of database issues and performance problems.
- Integration with Azure Security Services: Can be integrated with other Azure security tools for comprehensive threat protection and analysis.
Tips on using AdditionalFields column
- You will need to typecast data prior to performing further operations on it. For example, if a column exists called Perf1Sec_i as well as a property in AdditionalFields called Perf2Sec, and you want to calculate total perf by adding both values, use something like:
AzureDiagnostics | extend TotalPerfSec = Perf1Sec_i + toint(AdditionalFields.Perf2Sec) | ....
. - Use Where clauses to reduce the data volume to the smallest possible prior to writing any complex logic to significantly improve performance. TimeGenerated is one column that should always be reduced to the smallest possible window. In the case of AzureDiagnostics, an additional filter should also always be included at the top of the query around the resource types that are being queried using the ResourceType column.
- When querying very large volumes of data, it is sometimes more efficient to do a filter on AdditionalFields as a whole rather than parsing it. For example, for large volumes of data
AzureDiagnostics | where AdditionalFields has "Perf2Sec"
is often more efficient thanAzureDiagnostics | where isnotnull(toint(AdditionalFields.Perf2Sec))
.
Azure Diagnostics mode
The following services use Azure diagnostics mode for their resource logs and send data to the Azure Diagnostics table.
- Analysis Services
- Application Gateways
- Automation Accounts
- Azure Database for MariaDB servers
- Azure Database for MySQL servers
- Azure Database for PostgreSQL servers
- Azure Database for PostgreSQL servers v2
- Batch accounts
- CDN profiles
- Cognitive Services
- Data Lake Analytics
- DataLake Storage Gen1
- Device Provisioning Services
- Digital Twins
- Event Grid Topics
- Event Hubs
- ExpressRoute circuits
- Front Doors
- Integration accounts
- Key Vault
- Kubernetes services
- Load balancers
- Logic Apps
- Media services
- Network interfaces
- Network Security Groups
- P2S VPN Gateways
- Power BI Embedded
- Public IP addresses
- Recovery Services vaults(Site Recovery)
- Search services
- Service Bus
- SQL databases
- SQL managed Instances
- SQL servers
- Stream Analytics jobs
- Traffic Manager profiles
- Virtual networks
- Virtual network gateways
- VPN Gateways
Azure Diagnostics mode or resource-specific mode
Most Azure resources write data to the workspace in either Azure diagnostics or resource-specific mode without giving you a choice.
All Azure services will eventually use the resource-specific mode. As part of this transition, some resources allow you to select a mode in the diagnostic setting. Specify resource-specific mode for any new diagnostic settings because this mode makes the data easier to manage.
Note :The following services use either Azure diagnostics mode or resource-specific mode for their resource logs depending on their configuration. When they use resource-specific mode, they do not send data to the AzureDiagnostics table.
- API Management Services
- Azure Cosmos DB
- Data factories (V2)
- IoT Hub
- Recovery Services vaults(Backup)
- Firewalls
Categories
- Azure Resources
- Security
- Network
Solutions
- LogManagement
Resource types
- Application Gateways
- CDN Profiles
- Azure Cosmos DB
- Event Grid Topics
- Event Hubs
- Firewalls
- Key Vaults
- Kubernetes Services
- Recovery Services Vaults
- Service Bus
- Azure Database for MySQL Flexible Servers
- Azure Database for PostgreSQL Flexible Servers
- Media Services
- Analysis Services
- Batch Accounts
- Cognitive Services
- Event Grid Partner Namespaces
- Event Grid Partner Topics
- Event Grid System Topics
- Azure Arc Enabled Kubernetes
- Azure Arc Provisioned Clusters
- IoT Hub
- Logic Apps
- API Management services
- Automation account
- Data factories
- Data Lake Storage Gen1
- Data Lake Analytics
- Power BI Embedded
- SQL Managed Instances
- SQL Servers
- SQL Databases
- Azure Database for MySQL Servers
- Azure Database for PostgreSQL Servers
- Azure Database for PostgreSQL Servers V2
- Azure Database for MariaDB Servers
- Device Provisioning Services
- ExpressRoute Circuits
- Front Doors
- Network Interfaces
- Network Security Groups
- Public IP Addresses
- Traffic Manager Profiles
- Virtual Network Gateways
- Virtual Private Network Gateways
- Virtual Networks
- Search Services
- Stream Analytics jobs
Columns
Column | Type | Description |
---|---|---|
action_id_s | String | |
action_name_s | String | |
action_s | String | |
ActivityId_g | Guid | |
AdditionalFields | ||
AdHocOrScheduledJob_s | String | |
application_name_s | String | |
audit_schema_version_d | Double | |
avg_cpu_percent_s | String | |
avg_mean_time_s | String | |
backendHostname_s | String | |
Caller_s | String | |
callerId_s | String | |
CallerIPAddress | String | |
calls_s | String | |
Category | String | |
client_ip_s | String | |
clientInfo_s | String | |
clientIP_s | String | |
clientIp_s | String | |
clientIpAddress_s | String | |
clientPort_d | Double | |
code_s | String | |
collectionName_s | String | |
conditions_destinationIP_s | String | |
conditions_destinationPortRange_s | String | |
conditions_None_s | String | |
conditions_protocols_s | String | |
conditions_sourceIP_s | String | |
conditions_sourcePortRange_s | String | |
CorrelationId | String | |
count_executions_d | Double | |
cpu_time_d | Double | |
database_name_s | String | |
database_principal_name_s | String | |
DatabaseName_s | String | |
db_id_s | String | |
direction_s | String | |
dop_d | Double | |
duration_d | Double | |
duration_milliseconds_d | Double | |
DurationMs | BigInt | |
ElasticPoolName_s | String | |
endTime_t | DateTime | |
Environment_s | String | |
error_code_s | String | |
error_message_s | String | |
errorLevel_s | String | |
event_class_s | String | |
event_s | String | |
event_subclass_s | String | |
event_time_t | DateTime | |
EventName_s | String | |
execution_type_d | Double | |
executionInfo_endTime_t | DateTime | |
executionInfo_exitCode_d | Double | |
executionInfo_startTime_t | DateTime | |
host_s | String | |
httpMethod_s | String | |
httpStatus_d | Double | |
httpStatusCode_d | Double | |
httpStatusCode_s | String | |
httpVersion_s | String | |
id_s | String | |
identity_claim_appid_g | Guid | |
identity_claim_ipaddr_s | String | |
instanceId_s | String | |
interval_end_time_d | Double | |
interval_start_time_d | Double | |
ip_s | String | |
is_column_permission_s | String | |
isAccessPolicyMatch_b | Bool | |
JobDurationInSecs_s | String | |
JobFailureCode_s | String | |
JobId_g | Guid | |
jobId_s | String | |
JobOperation_s | String | |
JobOperationSubType_s | String | |
JobStartDateTime_s | String | |
JobStatus_s | String | |
JobUniqueId_g | Guid | |
Level | String | |
log_bytes_used_d | Double | |
logical_io_reads_d | Double | |
logical_io_writes_d | Double | |
LogicalServerName_s | String | |
macAddress_s | String | |
matchedConnections_d | Double | |
max_cpu_time_d | Double | |
max_dop_d | Double | |
max_duration_d | Double | |
max_log_bytes_used_d | Double | |
max_logical_io_reads_d | Double | |
max_logical_io_writes_d | Double | |
max_num_physical_io_reads_d | Double | |
max_physical_io_reads_d | Double | |
max_query_max_used_memory_d | Double | |
max_rowcount_d | Double | |
max_time_s | String | |
mean_time_s | String | |
Message | String | |
min_time_s | String | |
msg_s | String | |
num_physical_io_reads_d | Double | |
object_id_d | Double | |
object_name_s | String | |
OperationName | String | |
OperationVersion | String | |
partitionKey_s | String | |
physical_io_reads_d | Double | |
plan_id_d | Double | |
policy_s | String | |
policyMode_s | String | |
primaryIPv4Address_s | String | |
priority_d | Double | |
properties_enabledForDeployment_b | Bool | |
properties_enabledForDiskEncryption_b | Bool | |
properties_enabledForTemplateDeployment_b | Bool | |
properties_s | String | |
properties_sku_Family_s | String | |
properties_sku_Name_s | String | |
properties_tenantId_g | Guid | |
query_hash_s | String | |
query_id_d | Double | |
query_max_used_memory_d | Double | |
query_plan_hash_s | String | |
query_time_d | Double | |
querytext_s | String | |
receivedBytes_d | Double | |
Region_s | String | |
requestCharge_s | String | |
requestQuery_s | String | |
requestResourceId_s | String | |
requestResourceType_s | String | |
requestUri_s | String | |
reserved_storage_mb_s | String | |
Resource | String | |
resource_actionName_s | String | |
resource_location_s | String | |
resource_originRunId_s | String | |
resource_resourceGroupName_s | String | |
resource_runId_s | String | |
resource_subscriptionId_g | Guid | |
resource_triggerName_s | String | |
resource_workflowId_g | Guid | |
resource_workflowName_s | String | |
ResourceGroup | String | |
_ResourceId | String | A unique identifier for the resource that the record is associated with |
ResourceProvider | String | |
ResourceProvider | String | |
ResourceType | String | |
ResourceType | String | |
response_rows_d | Double | |
resultCode_s | String | |
ResultDescription | String | |
ResultDescription | String | |
resultDescription_ChildJobs_s | String | |
resultDescription_ErrorJobs_s | String | |
resultMessage_s | String | |
ResultSignature | String | |
ResultType | String | |
ResultType | String | |
rootCauseAnalysis_s | String | |
routingRuleName_s | String | |
rowcount_d | Double | |
ruleName_s | String | |
RunbookName_s | String | |
RunOn_s | String | |
schema_name_s | String | |
sentBytes_d | Double | |
sequence_group_id_g | Guid | |
sequence_number_d | Double | |
server_principal_sid_s | String | |
session_id_d | Double |