Bring your investigation queries with you across multiple workspaces
… by saving them inside a 'query pack'
Introduction
As a security analyst, responsible for performing triage as part of incident response, you're performing similar tasks quite regularly. Like checking a user by determining from where they logged on, how, and with what device as an example.
When you're using Microsoft Sentinel (and/or their Defender suite of products) you're probably getting quite familair with KQL to easily retrieve this information. And you've probably already collected a small arsenal of such investigation queries and saved them into the Log Analytics workspace for later use.
But what happens when you have to switch workspaces? All of your carefully saved queries are suddenly no longer available.
Larger enterprises tend to have multiple Sentinel workspaces to limit the amount of cross region traffic and the additional costs that come with it.
Please don't start duplicating your queries by saving them into every workspace you touch. To help with such tasks, Microsoft has you covered with query packs (preview). An Azure resource which helps you to centrally manage all of your queries and use them across all of your workspaces!
Query packs are not exclusive to Microsoft Sentinel-enabled workspaces. You can use them for all of your other Azure Monitor / Log Analytics workspaces as well.
Pack the queries!
Query packs are a type of Azure resource in which you can store, well … KQL queries. Queries inside those packs can be shared across multiple workspaces and even across multiple subscriptions. And with Azure Lighthouse, this will even work across multiple tenants as well!
If you save your first query from the portal, it will nowadays default to create a new blank query pack called DefaultQueryPack
in a resource group called loganalyticsdefaultresources
inside your subscription.
But you probably want to go ahead a deploy a query pack somewhere on a cetralized location and maintain it from there.
Queries can still be stored inside a Log Analytics Workspace (‘savedQueries’) but this is considered “legacy” since Microsoft introduced Query Packs @ Build 2021.
Using an ARM template
Since you want to centrally manage your queries, you might as well deploy your query pack by leveraging infrastructure-as-code and maintain it inside a Git repository. This way you can easily deploy it to multiple locations if needed (i.e. tenants outside your Lighthouse reach) and you'll benefit from version control on your queries.
I've prepared an ARM template for a single query pack with a couple of queries you can try out to get familiar with the functionality. Click the button below to get started:
Both the ARM template, as well as an example parameters file, are also available on my Github page.
Parameters
In the automated deployment above, you won't be using a separate parameters
file but I'd suggest you do so if you're planning to deploy query packs. This way you can put all of your KQL queries together in an array
and let the ARM template loop through them during the deployment.
During deployment you'll notice that the query pack itself and its underlying queries are deployed separately:
We see this with a lot of different Azure resources as well. But this can make it a little bit tricky to make use of the copy()
function inside the template if you're not careful. This is because the individual query "resources" obviously require a unique name
so you cannot just use a static string for it.
You also want this dynamic string to be same for that particular query in your array every time you deploy the template. If you would just use a guid()
function as part of the name, the deployment will be successful on the first try but, on the second it will fail. This is because it tries to deploy the same query as a new resource and it will fail because its displayName
will already exist.
For this reason I'd like to use the guid()
function, but provide an input which will reliably be the same every time you deploy the template. In this case the query's displayName
. Check it out below on line 12:
Using your new shiny query pack
Once a query pack exists in your environment you can select if from within your workspace.
Open up logs
→ Queries
and click on the dotted button that appeared right of the search
field:
Once selected you can find/sort your queries based on label, categories, resourceTypes and solutions depending on what you've provided within the definition of the parameters file:
For more details about all supported parameter values please check out the ARM reference on Microsoft docs.
Final words
So query packs can be a very usefull and handy way of storing your KQL queries on a centralized location. And with an automated deployment via ARM, you have full control on versioning and history.
I think that especially for larger enterprises or MSPs (Microsoft Service Providers) attending multiple workspaces across their customers. its a no-brainer to stop storing queries in individual workspaces and embrace the use of query packs. You can of course also distribute multiple query packs to be available for different teams.