The azure-native:securityinsights:ScheduledAlertRule resource, part of the Pulumi Azure Native provider, defines scheduled alert rules in Microsoft Sentinel that run KQL queries against Log Analytics data and create alerts when conditions are met. This guide focuses on three capabilities: query-based threat detection, entity extraction from log data, and incident grouping and correlation.
Alert rules run within a Log Analytics workspace and query ingested log data on a schedule. The examples are intentionally small. Combine them with your own KQL queries, entity mappings, and incident response workflows.
Define query-based alerts with entity extraction and incident grouping
Security teams monitor log data for suspicious patterns using KQL queries that run on a schedule. When the query returns results, Sentinel creates alerts and optionally groups them into incidents based on shared characteristics.
import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";
const scheduledAlertRule = new azure_native.securityinsights.ScheduledAlertRule("scheduledAlertRule", {
alertDetailsOverride: {
alertDescriptionFormat: "Suspicious activity was made by {{ComputerIP}}",
alertDisplayNameFormat: "Alert from {{Computer}}",
alertDynamicProperties: [
{
alertProperty: azure_native.securityinsights.AlertProperty.ProductComponentName,
value: "ProductComponentNameCustomColumn",
},
{
alertProperty: azure_native.securityinsights.AlertProperty.ProductName,
value: "ProductNameCustomColumn",
},
{
alertProperty: azure_native.securityinsights.AlertProperty.AlertLink,
value: "Link",
},
],
},
customDetails: {
OperatingSystemName: "OSName",
OperatingSystemType: "OSType",
},
description: "An example for a scheduled rule",
displayName: "My scheduled rule",
enabled: true,
entityMappings: [
{
entityType: azure_native.securityinsights.EntityMappingType.Host,
fieldMappings: [{
columnName: "Computer",
identifier: "FullName",
}],
},
{
entityType: azure_native.securityinsights.EntityMappingType.IP,
fieldMappings: [{
columnName: "ComputerIP",
identifier: "Address",
}],
},
],
eventGroupingSettings: {
aggregationKind: azure_native.securityinsights.EventGroupingAggregationKind.AlertPerResult,
},
incidentConfiguration: {
createIncident: true,
groupingConfiguration: {
enabled: true,
groupByAlertDetails: [azure_native.securityinsights.AlertDetail.DisplayName],
groupByCustomDetails: [
"OperatingSystemType",
"OperatingSystemName",
],
groupByEntities: [azure_native.securityinsights.EntityMappingType.Host],
lookbackDuration: "PT5H",
matchingMethod: azure_native.securityinsights.MatchingMethod.Selected,
reopenClosedIncident: false,
},
},
kind: "Scheduled",
query: "Heartbeat",
queryFrequency: "PT1H",
queryPeriod: "P2DT1H30M",
resourceGroupName: "myRg",
ruleId: "73e01a99-5cd7-4139-a149-9f2736ff2ab5",
severity: azure_native.securityinsights.AlertSeverity.High,
suppressionDuration: "PT1H",
suppressionEnabled: false,
tactics: [
azure_native.securityinsights.AttackTactic.Persistence,
azure_native.securityinsights.AttackTactic.LateralMovement,
],
triggerOperator: azure_native.securityinsights.TriggerOperator.GreaterThan,
triggerThreshold: 0,
workspaceName: "myWorkspace",
});
import pulumi
import pulumi_azure_native as azure_native
scheduled_alert_rule = azure_native.securityinsights.ScheduledAlertRule("scheduledAlertRule",
alert_details_override={
"alert_description_format": "Suspicious activity was made by {{ComputerIP}}",
"alert_display_name_format": "Alert from {{Computer}}",
"alert_dynamic_properties": [
{
"alert_property": azure_native.securityinsights.AlertProperty.PRODUCT_COMPONENT_NAME,
"value": "ProductComponentNameCustomColumn",
},
{
"alert_property": azure_native.securityinsights.AlertProperty.PRODUCT_NAME,
"value": "ProductNameCustomColumn",
},
{
"alert_property": azure_native.securityinsights.AlertProperty.ALERT_LINK,
"value": "Link",
},
],
},
custom_details={
"OperatingSystemName": "OSName",
"OperatingSystemType": "OSType",
},
description="An example for a scheduled rule",
display_name="My scheduled rule",
enabled=True,
entity_mappings=[
{
"entity_type": azure_native.securityinsights.EntityMappingType.HOST,
"field_mappings": [{
"column_name": "Computer",
"identifier": "FullName",
}],
},
{
"entity_type": azure_native.securityinsights.EntityMappingType.IP,
"field_mappings": [{
"column_name": "ComputerIP",
"identifier": "Address",
}],
},
],
event_grouping_settings={
"aggregation_kind": azure_native.securityinsights.EventGroupingAggregationKind.ALERT_PER_RESULT,
},
incident_configuration={
"create_incident": True,
"grouping_configuration": {
"enabled": True,
"group_by_alert_details": [azure_native.securityinsights.AlertDetail.DISPLAY_NAME],
"group_by_custom_details": [
"OperatingSystemType",
"OperatingSystemName",
],
"group_by_entities": [azure_native.securityinsights.EntityMappingType.HOST],
"lookback_duration": "PT5H",
"matching_method": azure_native.securityinsights.MatchingMethod.SELECTED,
"reopen_closed_incident": False,
},
},
kind="Scheduled",
query="Heartbeat",
query_frequency="PT1H",
query_period="P2DT1H30M",
resource_group_name="myRg",
rule_id="73e01a99-5cd7-4139-a149-9f2736ff2ab5",
severity=azure_native.securityinsights.AlertSeverity.HIGH,
suppression_duration="PT1H",
suppression_enabled=False,
tactics=[
azure_native.securityinsights.AttackTactic.PERSISTENCE,
azure_native.securityinsights.AttackTactic.LATERAL_MOVEMENT,
],
trigger_operator=azure_native.securityinsights.TriggerOperator.GREATER_THAN,
trigger_threshold=0,
workspace_name="myWorkspace")
package main
import (
securityinsights "github.com/pulumi/pulumi-azure-native-sdk/securityinsights/v3"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := securityinsights.NewScheduledAlertRule(ctx, "scheduledAlertRule", &securityinsights.ScheduledAlertRuleArgs{
AlertDetailsOverride: &securityinsights.AlertDetailsOverrideArgs{
AlertDescriptionFormat: pulumi.String("Suspicious activity was made by {{ComputerIP}}"),
AlertDisplayNameFormat: pulumi.String("Alert from {{Computer}}"),
AlertDynamicProperties: securityinsights.AlertPropertyMappingArray{
&securityinsights.AlertPropertyMappingArgs{
AlertProperty: pulumi.String(securityinsights.AlertPropertyProductComponentName),
Value: pulumi.String("ProductComponentNameCustomColumn"),
},
&securityinsights.AlertPropertyMappingArgs{
AlertProperty: pulumi.String(securityinsights.AlertPropertyProductName),
Value: pulumi.String("ProductNameCustomColumn"),
},
&securityinsights.AlertPropertyMappingArgs{
AlertProperty: pulumi.String(securityinsights.AlertPropertyAlertLink),
Value: pulumi.String("Link"),
},
},
},
CustomDetails: pulumi.StringMap{
"OperatingSystemName": pulumi.String("OSName"),
"OperatingSystemType": pulumi.String("OSType"),
},
Description: pulumi.String("An example for a scheduled rule"),
DisplayName: pulumi.String("My scheduled rule"),
Enabled: pulumi.Bool(true),
EntityMappings: securityinsights.EntityMappingArray{
&securityinsights.EntityMappingArgs{
EntityType: pulumi.String(securityinsights.EntityMappingTypeHost),
FieldMappings: securityinsights.FieldMappingArray{
&securityinsights.FieldMappingArgs{
ColumnName: pulumi.String("Computer"),
Identifier: pulumi.String("FullName"),
},
},
},
&securityinsights.EntityMappingArgs{
EntityType: pulumi.String(securityinsights.EntityMappingTypeIP),
FieldMappings: securityinsights.FieldMappingArray{
&securityinsights.FieldMappingArgs{
ColumnName: pulumi.String("ComputerIP"),
Identifier: pulumi.String("Address"),
},
},
},
},
EventGroupingSettings: &securityinsights.EventGroupingSettingsArgs{
AggregationKind: pulumi.String(securityinsights.EventGroupingAggregationKindAlertPerResult),
},
IncidentConfiguration: &securityinsights.IncidentConfigurationArgs{
CreateIncident: pulumi.Bool(true),
GroupingConfiguration: &securityinsights.GroupingConfigurationArgs{
Enabled: pulumi.Bool(true),
GroupByAlertDetails: pulumi.StringArray{
pulumi.String(securityinsights.AlertDetailDisplayName),
},
GroupByCustomDetails: pulumi.StringArray{
pulumi.String("OperatingSystemType"),
pulumi.String("OperatingSystemName"),
},
GroupByEntities: pulumi.StringArray{
pulumi.String(securityinsights.EntityMappingTypeHost),
},
LookbackDuration: pulumi.String("PT5H"),
MatchingMethod: pulumi.String(securityinsights.MatchingMethodSelected),
ReopenClosedIncident: pulumi.Bool(false),
},
},
Kind: pulumi.String("Scheduled"),
Query: pulumi.String("Heartbeat"),
QueryFrequency: pulumi.String("PT1H"),
QueryPeriod: pulumi.String("P2DT1H30M"),
ResourceGroupName: pulumi.String("myRg"),
RuleId: pulumi.String("73e01a99-5cd7-4139-a149-9f2736ff2ab5"),
Severity: pulumi.String(securityinsights.AlertSeverityHigh),
SuppressionDuration: pulumi.String("PT1H"),
SuppressionEnabled: pulumi.Bool(false),
Tactics: pulumi.StringArray{
pulumi.String(securityinsights.AttackTacticPersistence),
pulumi.String(securityinsights.AttackTacticLateralMovement),
},
TriggerOperator: securityinsights.TriggerOperatorGreaterThan,
TriggerThreshold: pulumi.Int(0),
WorkspaceName: pulumi.String("myWorkspace"),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;
return await Deployment.RunAsync(() =>
{
var scheduledAlertRule = new AzureNative.SecurityInsights.ScheduledAlertRule("scheduledAlertRule", new()
{
AlertDetailsOverride = new AzureNative.SecurityInsights.Inputs.AlertDetailsOverrideArgs
{
AlertDescriptionFormat = "Suspicious activity was made by {{ComputerIP}}",
AlertDisplayNameFormat = "Alert from {{Computer}}",
AlertDynamicProperties = new[]
{
new AzureNative.SecurityInsights.Inputs.AlertPropertyMappingArgs
{
AlertProperty = AzureNative.SecurityInsights.AlertProperty.ProductComponentName,
Value = "ProductComponentNameCustomColumn",
},
new AzureNative.SecurityInsights.Inputs.AlertPropertyMappingArgs
{
AlertProperty = AzureNative.SecurityInsights.AlertProperty.ProductName,
Value = "ProductNameCustomColumn",
},
new AzureNative.SecurityInsights.Inputs.AlertPropertyMappingArgs
{
AlertProperty = AzureNative.SecurityInsights.AlertProperty.AlertLink,
Value = "Link",
},
},
},
CustomDetails =
{
{ "OperatingSystemName", "OSName" },
{ "OperatingSystemType", "OSType" },
},
Description = "An example for a scheduled rule",
DisplayName = "My scheduled rule",
Enabled = true,
EntityMappings = new[]
{
new AzureNative.SecurityInsights.Inputs.EntityMappingArgs
{
EntityType = AzureNative.SecurityInsights.EntityMappingType.Host,
FieldMappings = new[]
{
new AzureNative.SecurityInsights.Inputs.FieldMappingArgs
{
ColumnName = "Computer",
Identifier = "FullName",
},
},
},
new AzureNative.SecurityInsights.Inputs.EntityMappingArgs
{
EntityType = AzureNative.SecurityInsights.EntityMappingType.IP,
FieldMappings = new[]
{
new AzureNative.SecurityInsights.Inputs.FieldMappingArgs
{
ColumnName = "ComputerIP",
Identifier = "Address",
},
},
},
},
EventGroupingSettings = new AzureNative.SecurityInsights.Inputs.EventGroupingSettingsArgs
{
AggregationKind = AzureNative.SecurityInsights.EventGroupingAggregationKind.AlertPerResult,
},
IncidentConfiguration = new AzureNative.SecurityInsights.Inputs.IncidentConfigurationArgs
{
CreateIncident = true,
GroupingConfiguration = new AzureNative.SecurityInsights.Inputs.GroupingConfigurationArgs
{
Enabled = true,
GroupByAlertDetails = new[]
{
AzureNative.SecurityInsights.AlertDetail.DisplayName,
},
GroupByCustomDetails = new[]
{
"OperatingSystemType",
"OperatingSystemName",
},
GroupByEntities = new[]
{
AzureNative.SecurityInsights.EntityMappingType.Host,
},
LookbackDuration = "PT5H",
MatchingMethod = AzureNative.SecurityInsights.MatchingMethod.Selected,
ReopenClosedIncident = false,
},
},
Kind = "Scheduled",
Query = "Heartbeat",
QueryFrequency = "PT1H",
QueryPeriod = "P2DT1H30M",
ResourceGroupName = "myRg",
RuleId = "73e01a99-5cd7-4139-a149-9f2736ff2ab5",
Severity = AzureNative.SecurityInsights.AlertSeverity.High,
SuppressionDuration = "PT1H",
SuppressionEnabled = false,
Tactics = new[]
{
AzureNative.SecurityInsights.AttackTactic.Persistence,
AzureNative.SecurityInsights.AttackTactic.LateralMovement,
},
TriggerOperator = AzureNative.SecurityInsights.TriggerOperator.GreaterThan,
TriggerThreshold = 0,
WorkspaceName = "myWorkspace",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.securityinsights.ScheduledAlertRule;
import com.pulumi.azurenative.securityinsights.ScheduledAlertRuleArgs;
import com.pulumi.azurenative.securityinsights.inputs.AlertDetailsOverrideArgs;
import com.pulumi.azurenative.securityinsights.inputs.EntityMappingArgs;
import com.pulumi.azurenative.securityinsights.inputs.EventGroupingSettingsArgs;
import com.pulumi.azurenative.securityinsights.inputs.IncidentConfigurationArgs;
import com.pulumi.azurenative.securityinsights.inputs.GroupingConfigurationArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var scheduledAlertRule = new ScheduledAlertRule("scheduledAlertRule", ScheduledAlertRuleArgs.builder()
.alertDetailsOverride(AlertDetailsOverrideArgs.builder()
.alertDescriptionFormat("Suspicious activity was made by {{ComputerIP}}")
.alertDisplayNameFormat("Alert from {{Computer}}")
.alertDynamicProperties(
AlertPropertyMappingArgs.builder()
.alertProperty("ProductComponentName")
.value("ProductComponentNameCustomColumn")
.build(),
AlertPropertyMappingArgs.builder()
.alertProperty("ProductName")
.value("ProductNameCustomColumn")
.build(),
AlertPropertyMappingArgs.builder()
.alertProperty("AlertLink")
.value("Link")
.build())
.build())
.customDetails(Map.ofEntries(
Map.entry("OperatingSystemName", "OSName"),
Map.entry("OperatingSystemType", "OSType")
))
.description("An example for a scheduled rule")
.displayName("My scheduled rule")
.enabled(true)
.entityMappings(
EntityMappingArgs.builder()
.entityType("Host")
.fieldMappings(FieldMappingArgs.builder()
.columnName("Computer")
.identifier("FullName")
.build())
.build(),
EntityMappingArgs.builder()
.entityType("IP")
.fieldMappings(FieldMappingArgs.builder()
.columnName("ComputerIP")
.identifier("Address")
.build())
.build())
.eventGroupingSettings(EventGroupingSettingsArgs.builder()
.aggregationKind("AlertPerResult")
.build())
.incidentConfiguration(IncidentConfigurationArgs.builder()
.createIncident(true)
.groupingConfiguration(GroupingConfigurationArgs.builder()
.enabled(true)
.groupByAlertDetails("DisplayName")
.groupByCustomDetails(
"OperatingSystemType",
"OperatingSystemName")
.groupByEntities("Host")
.lookbackDuration("PT5H")
.matchingMethod("Selected")
.reopenClosedIncident(false)
.build())
.build())
.kind("Scheduled")
.query("Heartbeat")
.queryFrequency("PT1H")
.queryPeriod("P2DT1H30M")
.resourceGroupName("myRg")
.ruleId("73e01a99-5cd7-4139-a149-9f2736ff2ab5")
.severity("High")
.suppressionDuration("PT1H")
.suppressionEnabled(false)
.tactics(
"Persistence",
"LateralMovement")
.triggerOperator("GreaterThan")
.triggerThreshold(0)
.workspaceName("myWorkspace")
.build());
}
}
resources:
scheduledAlertRule:
type: azure-native:securityinsights:ScheduledAlertRule
properties:
alertDetailsOverride:
alertDescriptionFormat: Suspicious activity was made by {{ComputerIP}}
alertDisplayNameFormat: Alert from {{Computer}}
alertDynamicProperties:
- alertProperty: ProductComponentName
value: ProductComponentNameCustomColumn
- alertProperty: ProductName
value: ProductNameCustomColumn
- alertProperty: AlertLink
value: Link
customDetails:
OperatingSystemName: OSName
OperatingSystemType: OSType
description: An example for a scheduled rule
displayName: My scheduled rule
enabled: true
entityMappings:
- entityType: Host
fieldMappings:
- columnName: Computer
identifier: FullName
- entityType: IP
fieldMappings:
- columnName: ComputerIP
identifier: Address
eventGroupingSettings:
aggregationKind: AlertPerResult
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
groupByAlertDetails:
- DisplayName
groupByCustomDetails:
- OperatingSystemType
- OperatingSystemName
groupByEntities:
- Host
lookbackDuration: PT5H
matchingMethod: Selected
reopenClosedIncident: false
kind: Scheduled
query: Heartbeat
queryFrequency: PT1H
queryPeriod: P2DT1H30M
resourceGroupName: myRg
ruleId: 73e01a99-5cd7-4139-a149-9f2736ff2ab5
severity: High
suppressionDuration: PT1H
suppressionEnabled: false
tactics:
- Persistence
- LateralMovement
triggerOperator: GreaterThan
triggerThreshold: 0
workspaceName: myWorkspace
The query property contains the KQL statement that runs against your Log Analytics workspace. The queryFrequency sets how often the rule executes (here, every hour), while queryPeriod defines the time window to analyze (2 days, 1 hour, 30 minutes). The triggerOperator and triggerThreshold determine when alerts fire; in this case, any result count greater than zero triggers an alert.
Entity mappings extract structured data from query results. The entityMappings array defines how columns map to entity types (Host, IP) and their identifiers. This allows Sentinel to correlate alerts across different data sources by recognizing the same entities.
The incidentConfiguration controls whether alerts become incidents and how they group together. When groupingConfiguration is enabled, Sentinel correlates alerts within a lookback window (5 hours here) based on matching entities, alert details, or custom fields. This reduces alert fatigue by consolidating related events into single incidents.
Beyond these examples
These snippets focus on specific scheduled alert rule features: KQL query scheduling and threshold evaluation, entity extraction from query results, and incident creation and grouping logic. They’re intentionally minimal rather than full security monitoring solutions.
The examples reference pre-existing infrastructure such as Log Analytics workspace with ingested data and resource group for Sentinel resources. They focus on configuring the alert rule rather than provisioning the workspace or data connectors.
To keep things focused, common alert rule patterns are omitted, including:
- Alert rule templates and versioning (alertRuleTemplateName, templateVersion)
- MITRE ATT&CK techniques mapping (techniques property)
- Alert suppression for duplicate events
- Custom alert formatting beyond entity mappings
These omissions are intentional: the goal is to illustrate how each alert rule feature is wired, not provide drop-in security monitoring modules. See the ScheduledAlertRule resource reference for all available configuration options.
Let's configure Azure Scheduled Alert Rules
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Configuration & Immutability
resourceGroupName, ruleId, and workspaceName properties are immutable and cannot be modified after creation. Changing these requires recreating the resource.Query Scheduling & Timing
queryFrequency defines how often the rule runs (e.g., PT1H runs every hour), while queryPeriod defines the time window the query looks back at (e.g., P2DT1H30M looks back 2 days, 1 hour, 30 minutes). Both use ISO 8601 duration format.P for period, T for time separator, followed by numbers and units (D=days, H=hours, M=minutes). Examples: PT1H = 1 hour, P2DT1H30M = 2 days 1 hour 30 minutes, PT5H = 5 hours.triggerOperator and triggerThreshold together. For example, triggerOperator: GreaterThan with triggerThreshold: 0 triggers an alert when the query returns more than 0 results.Alert Customization & Enrichment
alertDetailsOverride with alertDisplayNameFormat and alertDescriptionFormat. Reference query columns using double-brace syntax like {{ColumnName}} (e.g., "Alert from {{Computer}}").customDetails as a key-value map where keys are custom field names and values are query column names. For example, OperatingSystemName: "OSName" adds a custom field using the OSName column from your query.alertDetailsOverride.alertDynamicProperties with alertProperty (e.g., ProductName, AlertLink) and value pointing to a query column name.Entity Mapping
entityMappings with entityType (e.g., Host, IP) and fieldMappings that map query column names to entity identifiers. For example, map the Computer column to the FullName identifier for Host entities.columnName values don’t exactly match columns returned by your query. Ensure your query returns all columns referenced in fieldMappings.Incident & Alert Grouping
eventGroupingSettings controls how query results are grouped into alerts (e.g., AlertPerResult creates one alert per row). incidentConfiguration.groupingConfiguration controls how multiple alerts are grouped into incidents based on criteria like entities or custom details.incidentConfiguration.groupingConfiguration with enabled: true, then specify grouping criteria using groupByAlertDetails, groupByCustomDetails, or groupByEntities. Set lookbackDuration (e.g., PT5H) to define the time window for grouping.suppressionEnabled is true, the rule won’t trigger duplicate alerts for the duration specified in suppressionDuration (e.g., PT1H suppresses for 1 hour) after the last trigger.