Exploring artifacts of a desired state to build health checks with limited access

· 3 min read

So this time around I’m writing about what I will refer to as an architecture pattern.

Concept

Basically, the concept is that you have some sort of existing access to a system and instead of adding more access or creating new service principals to access the correct API, you can use your existing access to look for artifacts of the desired state you want.

It might also be the case that the feature you are looking to monitor has no dedicated API yet, so you’re left waiting for something to be created.

graph LR
    subgraph "Managing tenant"
    p[Existing principal]
    NP[New principal]
    end
    subgraph "Managed tenant"
    NP -.->  NA
    p -.-> NA
    a[Existing access] --> s[Scope]
    NA[New access] -.-> NR
    subgraph "New scope"
    NR[New resource]
    end
    end
    p --> a

Basically, there’s something we want to check, but it’s outside our current access. We can either create a new principal with new access, or add new access on an existing principal, but sometimes that can take time.

Now, the idea explored in this post hinges on the fact that the new resource produces artifacts reachable by the existing access.

graph LR
    subgraph "Managing tenant"
    p[Existing principal]
    end
    subgraph "Managed tenant"
    a[Existing access]
    subgraph "Scope"
    a -->|access to| AF[Artifact]
    end
    subgraph "New scope"
    NR[New resource] -.-> |produces| AF
    end
    end
    p --> a

Example

An apt of example of this is Microsoft Sentinel because it has some settings that historically has been unaccessible by any API.

There are still examples of this, but one that was recently fixed since api version 2025-07-01-preview is Product Settings now having API-support.

Before that, we could use the pattern described above to look for the BehaviorAnalytics-table that stores UEBA’s output information. Basically, if UEBA is turned on, this table should exists and there should be data in it. There are probably other methods like Azure Resource Graph queries that could be explored. That’s just another pattern though.

My assumption going forward is that as an external or internal SOC and/or SecOps team you would have an existing form of access to Microsoft Sentinel.

Let’s assume that access is via service principal for deploying resources that’s assigned via Azure Lighthouse. I’ve written in detail about Azure Lighthouse before, but a quick tl;dr of how this access pattern would look is something like this:

graph LR
    subgraph "Managing tenant"
    p[Principal]
    end
    subgraph "Managed tenant"
    a[Access] --> s[Scope]
    end
    p --> a

In essence, Azure Lighthouse gives a principal in the managing tenant a level of access to a scope in the managed tenant:

For this experiment, the principal is a SPN. The access is defined by two Azure RBAC built-in roles, namely Log Analytics Contributor and Microsoft Sentinel Contributor.

Rounding out the required trio we have the scope which is a resource group where the workspace lives. As you can see, it aligns closely with the previous examples presented as diagrams.

I’m also going to use magic to turn back time to the point before Product Settings was surfaced in the securityinsights-api.

If we now apply the pattern discussed previously in this blog, the thing we want to find out is if UEBA is enabled, or not. So with our constraints being inside our current access, one way of solving this is looking for an artifact produced by UEBA being enabled. It looks like this:

graph LR
    subgraph "Managing tenant"
    p[Existing principal]
    end
    subgraph "Managed tenant"
    a[Existing access]
    subgraph "Scope"
    a -->|access to| AF[BehaviorAnalytics table]
    end
    subgraph "Microsoft Sentinel Product Settings"
    NR[UEBA enabled] -.-> |produces| AF
    end
    end
    p --> a

Practically, this is achieved by using either Azure Powershell or the Azure API in some way with our SPN to run a KQL query.

BehaviorAnalytics
| where TimeGenerated > ago(1d)
| take 1

Adjust TimeGenerated based on your use case.


Conclusion

And so we don’t really need to check if the switch is toggled on or off, we simply need to check if there’s light on.

iwantitthatway I can’t go a single post without gifs being involved, it’s a sickness I swear.

This is not really revolutionary, but hopefully it can help when designing systems for least privilege to think creatively about how we obtain access. This pattern also works for a lot of other tools and features, such as monitoring log ingestion (is there data in the table, recently?) and some of the features in the Defender portal that still lacks API-support.