↳ GitHub sourceAnalytics ruleMedium
Suspicious Entra ID Joined Device Update
Description
'This query looks for suspicious updates to an Microsoft Entra ID joined device where the device name is changed and the device falls out of compliance.
This could occur when a threat actor updates the details of an Autopilot provisioned device using a stolen device ticket, in order to access certificates and keys.
Ref: https://dirkjanm.io/assets/raw/Insomnihack%20Breaking%20and%20fixing%20Azure%20AD%20device%20identity%20security.pdf'
- Rule type
- Scheduled
- Version
- 1.0.4
- Declared status
- Available
- Query frequency
- 1d
- Query period
- 1d
- Trigger
- gt 0
Declared MITRE coverage
Declared sources
Metadata from the source file. No dependencies inferred from KQL.
Connectors
Data types
KQL query
Original query, unchanged.
AuditLogs
| where OperationName =~ "Update device"
| mv-apply TargetResource=TargetResources on (
where TargetResource.type =~ "Device"
| extend ModifiedProperties = TargetResource.modifiedProperties
| extend DeviceId = TargetResource.id)
| mv-apply Prop=ModifiedProperties on (
where Prop.displayName =~ "CloudDisplayName"
| extend OldName = Prop.oldValue
| extend NewName = Prop.newValue)
| mv-apply Prop=ModifiedProperties on (
where Prop.displayName =~ "IsCompliant"
| extend OldComplianceState = Prop.oldValue
| extend NewComplianceState = Prop.newValue)
| mv-apply Prop=ModifiedProperties on (
where Prop.displayName =~ "TargetId.DeviceTrustType"
| extend OldTrustType = Prop.oldValue
| extend NewTrustType = Prop.newValue)
| mv-apply Prop=ModifiedProperties on (
where Prop.displayName =~ "Included Updated Properties"
| extend UpdatedProperties = Prop.newValue)
| extend OldDeviceName = tostring(parse_json(tostring(OldName))[0])
| extend NewDeviceName = tostring(parse_json(tostring(NewName))[0])
| extend OldComplianceState = tostring(parse_json(tostring(OldComplianceState))[0])
| extend NewComplianceState = tostring(parse_json(tostring(NewComplianceState))[0])
| extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
| extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
| extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
| extend InitiatingIpAddress = tostring(iff(isnotempty(InitiatedBy.user.ipAddress), InitiatedBy.user.ipAddress, InitiatedBy.app.ipAddress))
| extend UpdatedPropertiesCount = array_length(split(UpdatedProperties, ','))
| where OldDeviceName != NewDeviceName
| where OldComplianceState =~ 'true' and NewComplianceState =~ 'false'
// Most common is transferring from AAD Registered to AAD Joined - we just want AAD Joined devices
| where NewTrustType == '"AzureAd"' and OldTrustType != '"Workplace"'
// We can modify this value to tune FPs - more properties changed about the device beyond its name the more suspicious it could be
| where UpdatedPropertiesCount > 1
| project-reorder TimeGenerated, DeviceId, NewDeviceName, OldDeviceName, NewComplianceState, InitiatingUserPrincipalName, InitiatingAadUserId, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingIpAddress, AADOperationType, OldTrustType, NewTrustType, UpdatedProperties, UpdatedPropertiesCount
| extend InitiatedByName = tostring(split(InitiatingUserPrincipalName,'@',0)[0]), InitiatedByUPNSuffix = tostring(split(InitiatingUserPrincipalName,'@',1)[0])
Declared entities
Related content
Links established from declared identifiers and solution manifests.
Source provenance
GitHubDisplayed values come from files in Azure/Azure-Sentinel. They describe the published template, not your workspace configuration.
- Commit
9800e51↗- Source identifier
3a3c6835-0086-40ca-b033-a93bf26d878f
GSTEP / CATALOG TRACKING
Added to catalog : 16 Sept 2026 · 05:49 UTC
Last change observed : 16 Sept 2026 · 05:49 UTC