Why are my Intune devices no longer compliant? Rolling out new compliance policies, raising minimum OS versions, or adjusting other controls can all cause devices to drift out of compliance. Ultimately, this impacts resource access whenever Conditional Access enforces a compliant device.
If you forward the IntuneOperationalLogs to a Log Analytics workspace1, you can query, parse, and alert on non-compliance events with just a few lines of KQL:
Anonymized example of noncompliant devices IntuneOperationalLogs | where OperationName == "Compliance" | extend Properties = parse_json(Properties) | evaluate bag_unpack(Properties) | where AlertType == @"Managed Device Not Compliant" | extend UserPrincipalName = iif(UserDisplayName != "System account", strcat(UserName, '@', UPNSuffix), "System account") // Extract the reason | extend ReasonRaw = tostring(split(Description, '||')[0]) // Parse the compliance Policy ID | parse ReasonRaw with ReasonParsed:string "_IID_" PolicyIdRaw:string | extend Reason = coalesce(ReasonParsed, ReasonRaw) | extend PolicyId = coalesce(PolicyIdRaw, 'DefaultDeviceCompliancePolicy') | project-away *Raw | project-reorder TimeGenerated, UserPrincipalName, DeviceHostName, IntuneDeviceId ,Reason, PolicyId Note The Properties column is a serialized JSON string that holds all the non-compliance details. The bag_unpack plugin2 expands every property in the bag into its own column, which keeps the rest of the query much simpler.
Managing Microsoft Sentinel table retention and tiers is typically done through scripts or the portal, but neither approach fits well into an infrastructure-as-code workflow and it can be difficult to maintain tables at scale. While exploring the Log Analytics Bicep resource provider, I came across the Microsoft.OperationalInsights/workspaces/tables resource type1, which makes it possible to manage table tier and retention declaratively with Bicep: version-controlled, reviewable, and reproducible across environments.
For each table we typically want to control:
Table tier: Analytics or Data Lake (Auxiliary) Interactive retention: the hot, queryable period Total retention: the overall retention period of the table Typical Table Configuration Settings Managing the total retention per table is necessary whenever you want to keep logs longer than the 90-day default to satisfy logging or compliance requirements. Since there is no workspace-level default for total retention, it has to be configured table by table, a perfect fit for a declarative approach.
Identify tables with recent ingestion # To identify ‘active’ tables, both a KQL- and API-based approach exist.
KQL # The following KQL query lists tables that have received data in the last 90 days along with their current tier, which is helpful when deciding what to put under Bicep management:
In response to CVE-2026-416151 (Microsoft Authenticator Information Disclosure Vulnerability), Microsoft started exposing the used Microsoft Authenticator app details as part of the Entra ID Sign-In Logs in the AuthenticationAppDeviceDetails column. The information can be queried via KQL. Vulnerable builds include versions prior to 6.2605.2973 (Android) and 6.8.47 (iOS), which have been patched.
You can use the below KQL query to find users with outdated Microsoft Authenticator app versions, which are vulnerable:
// https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-41615 let MinimumVersions = datatable( AuthenticatorOperatingSystem: string, PatchedAuthenticatorVersion: string )[ "Android", "6.2605.2973", "Ios", "6.8.47" ]; SigninLogs | where isnotempty(AuthenticationAppDeviceDetails) | extend AuthenticationAppDetails = parse_json(AuthenticationAppDeviceDetails) | extend AuthenticatorOperatingSystem = tostring(AuthenticationAppDetails.operatingSystem) | extend UsedAuthenticatorVersion = tostring(AuthenticationAppDetails.appVersion) // b2b and guest accounts include: {"deviceId":"{PII Removed}"} and no authenticator details | where isnotempty(UsedAuthenticatorVersion) | join kind=leftouter MinimumVersions on AuthenticatorOperatingSystem | extend isVulnerable = parse_version(UsedAuthenticatorVersion) < parse_version(PatchedAuthenticatorVersion) | where isVulnerable | distinct UserPrincipalName, AuthenticatorOperatingSystem, UsedAuthenticatorVersion, isVulnerable The AuthenticationAppDeviceDetails (JSON) column itself consists of the following properties:
appVersion clientApp deviceId operatingSystem The clientApp property is really helpful, as we now also have another option to identify users who use the Authenticator light capabilities, available as part of the Outlook app:
SigninLogs | where isnotempty(AuthenticationAppDeviceDetails) | extend AuthenticationAppDetails = parse_json(AuthenticationAppDeviceDetails) | extend AuthenticationAppDetailsClientApp = tostring(AuthenticationAppDetails.clientApp) | where AuthenticationAppDetailsClientApp == "Outlook" | distinct UserPrincipalName, AuthenticationAppDetailsClientApp This might be relevant in your environment if you did not disable the Microsoft-managed setting for using the Authenticator light option, which, for example, does not support Conditional Access authentication strengths, passkeys, and app protection policies:
The Microsoft Sentinel SOAR playbook generator has mostly flown under the radar since it entered public preview, so let’s look at what it can do and whether it can replace Logic Apps with something more native. In SecOps conversations, I often hear the same view: Logic Apps are fine, but if you already script or code, they can feel like overhead rather than a productivity boost. Many MSSPs also rely on more flexible tooling such as Azure Functions or compiled code for incident automation.
The new Sentinel playbook generator feels more native for Defender XDR and Sentinel integration, and it adds AI on top. Let’s see whether that actually helps.
Playbook Generator in Action # You can find the playbook generator in the Unified Security Operations Platform (USOP) under the Sentinel automation blade, via the ‘Playbook Generator β¨’ option.
It opens a Visual Studio Code or GitHub Codespaces-style editor with Cline preinstalled for building Python playbooks.
Creating a playbook with natural language is straightforward. Cline uses a plan-and-act flow: you describe what you want, then it tests and builds the playbook.
For my initial tests, I used the following prompt:
All too often, my baseVISION (IR) colleagues and I find compromised cloud accounts where many security ‘signals’ were missedβboth from a prevention and detection perspective. In this blog post, I want to share some motivation and tips to help you adopt Entra ID Protection risk-based Conditional Access policies to increase your tenant’s security posture, and ensure you don’t miss the next obvious account breach.
Real-world motivation # Anyone who has seen an AiTM campaign in the wild will probably notice the following details from the Entra ID sign-in logs of a compromised account:
ResourceDisplayName: OfficeHome UserAgent: axios/1.13.2 The problem? Even though Entra ID Protection flagged the sign-in as ‘High’ risk, the user could still sign in because MFA automatically remediated the sign-in risk (userPassedMfaDrivenByRiskBasedPolicy).
The bummer? # The environment had a Conditional Access policy in place that would have blocked users with high sign-in risk, but the policy was only in report-only mode, so the user could sign in without any issues.