Manage Microsoft Defender (unified security, security.microsoft.com) alerts and incidents through
the Microsoft Graph security API, using the Microsoft/msgraph
provider. Triage existing incidents and alerts, add incident comments, manage custom detection
rules, and (experimentally) attempt incident and alert creation, with a v1.0 or beta toggle per
operation.
Status: core, untested. This module has not been run against a live tenant yet (it needs a Defender / E5 licence and the security Graph permissions). It is published as the core surface to be validated and released once a licensed tenant is available. CI self-test and release are held until then.
Microsoft Sentinel now surfaces its incidents and alerts in the Microsoft Defender portal
(security.microsoft.com); the workspace is still deployed in Azure, but the incident experience
moved. The Graph /security/incidents and /security/alerts_v2 endpoints are that unified
Defender surface, whereas the older Microsoft.SecurityInsights (Azure) API is the Azure-portal
one. This module targets Graph so the changes land where analysts now work.
- Update / triage incidents and alerts: documented and stable on
v1.0(PATCH). - Comments on incidents: documented (
POST /security/incidents/{id}/comments). - Custom detection rules (
security/rules/detectionRules, beta): the documented way to have custom alerts and incidents generated into the Defender portal. Managed here viasecurity_resources. - Creating a manual alert (
createManualAlert,POST /security/alerts_v2, beta): documented. This is a real, supported create and the way to raise a custom alert that rolls up into a Defender incident. Managed here viamanual_alerts(title, description, severity and category are required). - Creating incidents directly: experimental and unverified. Microsoft documents incidents as
system-generated and publishes no create operation, but the beta metadata exposes a
POSTon the collection. Theincidentsinput attempts thatPOSTso it can be tested against a licensed tenant; do not rely on it until you have.
| Input | Operation | Graph resource used |
|---|---|---|
incident_updates |
PATCH an existing incident | msgraph_update_resource (destroy is a no-op) |
incident_comments |
POST a comment | msgraph_resource_action |
alert_updates |
PATCH an existing alert | msgraph_update_resource |
manual_alerts |
create a manual alert (createManualAlert, documented) | msgraph_resource |
security_resources |
full CRUD on any security resource (detection rules, ...) | msgraph_resource |
incidents |
experimental incident create (beta, unverified) | msgraph_resource |
All as Microsoft Graph application permissions, admin-consented, on a Defender / E5 tenant:
| Feature | Minimum permission |
|---|---|
| Read/update incidents and comments | SecurityIncident.ReadWrite.All |
| Read/update alerts (alerts_v2) | SecurityAlert.ReadWrite.All |
| Custom detection rules | CustomDetection.ReadWrite.All |
examples/minimal- triage one existing incident.examples/complete- triage an incident and alert, add a comment, create a custom detection rule, and (behind a flag) attempt the experimental incident create.
| Name | Version |
|---|---|
| terraform | >= 1.9.0, < 2.0.0 |
| msgraph | >= 0.1.0, < 1.0.0 |
| Name | Version |
|---|---|
| msgraph | >= 0.1.0, < 1.0.0 |
No modules.
| Name | Type |
|---|---|
| msgraph_resource.incidents | resource |
| msgraph_resource.manual_alerts | resource |
| msgraph_resource.security_resources | resource |
| msgraph_resource_action.incident_comments | resource |
| msgraph_update_resource.alert_updates | resource |
| msgraph_update_resource.incident_updates | resource |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| alert_updates | Existing alerts (alerts_v2) to update (triage), keyed by a stable logical name. Patches the alert identified by alert_id. Typed fields cover the common updatable properties; use body for anything else (merged over the typed fields). |
map(object({ |
{} |
no |
| default_api_version | Default Microsoft Graph API version for operations that do not set their own. One of "v1.0" or "beta". Update and comment operations are documented on v1.0 (stable) and default to it. The create operations (incidents, alerts) are only exposed on beta, so they default to beta regardless of this value unless you pin api_version on the entry. |
string |
"v1.0" |
no |
| incident_comments | Comments to add to existing incidents, keyed by a stable logical name. Each posts one comment to the incident identified by incident_id. Adding a comment is a one-time action (not removed on destroy). | map(object({ |
{} |
no |
| incident_updates | Existing security incidents to update (triage), keyed by a stable logical name. Patches the incident identified by incident_id. The typed fields cover the common updatable properties; use body to set anything else (it is merged over the typed fields). |
map(object({ |
{} |
no |
| incidents | Security incidents to create and manage, keyed by a stable logical name. EXPERIMENTAL: creating incidents through Graph is a beta, undocumented capability; verify it against your tenant. body is the raw incident object (for example displayName, severity, status, classification, determination, customTags). api_version defaults to beta because create is beta only. |
map(object({ |
{} |
no |
| manual_alerts | Manual alerts to create via the createManualAlert operation (POST /security/alerts_v2), keyed by a stable logical name. The module always sends the required @odata.type (#microsoft.graph.security.manualAlert). title, description, severity, category and entity_definitions (1 to 100) are required by the API; the rest are optional. Anything else can be set through body (merged over the typed fields). Creating a manual alert makes Defender open (or, with link_to_incident, link to) an incident. api_version defaults to beta. |
map(object({ |
related })), []) recommended_actions = optional(string) mitre_techniques = optional(list(string)) sentinel_workspace = optional(string) # route the alert to a Sentinel workspace link_to_incident = optional(number) # link to an existing incident id instead of a new one is_excluded_from_correlation = optional(bool) body = optional(any) api_version = optional(string) update_method = optional(string) response_export_values = optional(map(string)) })) |
{} |
| security_resources | Arbitrary Graph security resources to manage with full CRUD, keyed by a stable logical name. url is the collection URL (for example "security/rules/detectionRules"), body is the resource object. Use this for endpoints without a first-class input above, notably custom detection rules, which generate alerts and incidents that surface in the Defender portal. |
map(object({ |
{} |
no |
| Name | Description |
|---|---|
| alert_update_ids | Map of alert-update key to the target alert resource id. |
| alert_update_outputs | Map of alert-update key to its exported response values. |
| incident_comment_outputs | Map of incident-comment key to the action response. |
| incident_ids | Map of incident key to the created incident resource id (only for incidents created via the experimental create path). |
| incident_update_ids | Map of incident-update key to the target incident resource id. |
| incident_update_outputs | Map of incident-update key to its exported response values. |
| incidents | Map of incident key to its resource url and exported response values (create path). |
| manual_alert_ids | Map of manual-alert key to the created alert resource id. |
| manual_alerts | Map of manual-alert key to its resource url and exported response values. |
| security_resources | Map of security-resource key to its resource url and exported response values. |