MEM Monitoring: Monitor Security baselines in Endpoint Security

As Sander Rozemuller and I join forces at the Experts Live event later this year on the topic Monitor Intune with API from different perspectives I read his approach to monitoring the Security Baselines which are part of the Endpoint Security part of Microsoft Intune.

If you are using the security baselines, it is important to keep track of new versions of the baselines. If a new version is available, you want to review the (new/ changed) baseline settings and apply these to your devices. But out of the box, there is no notification for this.

Besides that, if you want to keep your environment clean up, you might also want to get notified when you gave baseline profiles that are not assigned.

Sander uses PowerShell scripting to get the job done, of course, I will use a Logic Apps flow to get the job done.

This blog post is part of the MEM (Intune) Monitoring series. An article with a short explanation of every MEM Monitoring flow I shared and links to the related articles can be found here.

Let’s see what this (small) Logic Apps flow looks like.

The solution in short

I used a Logic Apps flow in Azure which runs on an occurrence base (once a week in this example). The flow has two branches. Branch one retrieves via a Graph API query the available Intune Security baselines. In case the published data is from the last 7 days and the baseline is used, it sends a notification. Because this means the security baseline is released since we last run the flow and this is a new baseline.

The second branch checks if the security baseline profiles are (still assigned). If we have a profile that is not assigned, a notification is sent.

The solution can be easily deployed with Bicep files, which can be found on my GitHub repo.

Requirements

We don’t have many requirements for this simple flow, we only need an Azure Managed Identity. The managed identity should have enough permissions to query Microsoft Graph for the required information.
The required Graph (application) permissions needed are DeviceManagementConfiguration.Read.All.

More information on creating a Managed identity can be found here.

Setup the first part of the Logic App flow

When the Managed identity is created, we can start creating the first part the flow.

Sign in to the Azure portal and open the Logic App service. I created a blank Logic App of type Consumption.

When the flow is created, click on the name of the flow at the top of the screen, open the Identity section, and on the tab User assigned add your Managed Identity.

Open the Overview tab, which shows a few templates, and choose Recurrence.

Change the interval settings to your needs.

We now add our first HTTP action.
As Method select GET.
As URI enter:

https://graph.microsoft.com/beta/deviceManagement/templates?$filter=(templateType%20eq%20'securityBaseline')%20or%20(templateType%20eq%20'advancedThreatProtectionSecurityBaseline')%20or%20(templateType%20eq%20'microsoftEdgeSecurityBaseline')%20or%20(templateType%20eq%20'cloudPC')

Choose Add Parameter and select Authentication.
As Authentication type select Managed identity.
Select your Managed identity from the list.
And add https://graph.microsoft.com as Audience.

Next, we need to add a new Parse JSON action. We parse the output of the HTTP action, to be able to use the values later on in the flow.
As Content, we select Body from the Dynamic content list that is from our HTTP action.
As Schema, we can run the current flow and grab the body from the HTTP action and add it via the Use sample payload option. We can also grab the body when we run the same query via Graph Explorer.

This is the schema:

{
    "properties": {
        "@@odata.context": {
            "type": "string"
        },
        "value": {
            "items": {
                "properties": {
                    "@@odata.type": {
                        "type": "string"
                    },
                    "description": {
                        "type": "string"
                    },
                    "displayName": {
                        "type": "string"
                    },
                    "id": {
                        "type": "string"
                    },
                    "intentCount": {
                        "type": "integer"
                    },
                    "isDeprecated": {
                        "type": "boolean"
                    },
                    "platformType": {
                        "type": "string"
                    },
                    "publishedDateTime": {
                        "type": "string"
                    },
                    "templateSubtype": {
                        "type": "string"
                    },
                    "templateType": {
                        "type": "string"
                    },
                    "versionInfo": {
                        "type": "string"
                    }
                },
                "required": [
                    "@@odata.type",
                    "id",
                    "displayName",
                    "description",
                    "versionInfo",
                    "isDeprecated",
                    "intentCount",
                    "templateType",
                    "platformType",
                    "templateSubtype",
                    "publishedDateTime"
                ],
                "type": "object"
            },
            "type": "array"
        }
    },
    "type": "object"
}

Now we add a Condition action which is a Control action.
In the Condition action, we check if the Baseline does have at least one profile under it (using the intentCount) and check if the baseline is recently released (using the publishedDateTime).

In the left box, add intentCount found via Dynamic content, this adds the condition in a new For each action. Choose is greater than from the drop-down en enter 0 to the right box.

Click Add, to add a new Row.
in the left box add publishedDateTime and choose is great than. In the right box we need to enter the current date minus 7 days (if the flow runs once a week). This is done using an Expression:

adddays(utcNow(‘yyyy-MM-ddTHH:mm:ssZ’),-7)

This is our condition action.

If both conditions are met, we want to receive a notification. This can be an email for example, or a message in a Teams channel, like the below example via a Teams webhook.

Enter an HTTP Action under True and as Method choose POST.
Enter your Teams webhook URL in the URI field. In the Body field we enter the text we like to send to the Teams channel. We can use dynamic content (variables) from the previous PARSE JSON action and enter our own text.
Use \n\n in the text to create new lines in the text message, otherwise, the message consists of one long line of text.
And I used ** to get some text in bold.

This is our first part of the flow.

Setup the second part of the Logic App flow

Now we add another branch to our flow, to check if the Security baseline contains profiles and if these are assigned. If the profile is not assigned, a notification is sent so we can clean up the unused profile.

Between the Recurrence and HTTP action, click on the Plus sign and choose Add a parallel branch.

We add an HTTP action.
As Method select GET.
As URI enter:

https://graph.microsoft.com/beta/deviceManagement/intents?$filter=contains(displayName,'baseline')%20or%20contains(displayName,'Baseline')

Don’t forget to add the authentication information.

We need to Parse the information, so add a Parse JSON action.
In the Content, field add Body from the HTTP action and enter the schema.

My used schema:

{
    "properties": {
        "@@odata.context": {
            "type": "string"
        },
        "value": {
            "items": {
                "properties": {
                    "description": {
                        "type": "string"
                    },
                    "displayName": {
                        "type": "string"
                    },
                    "id": {
                        "type": "string"
                    },
                    "isAssigned": {
                        "type": "boolean"
                    },
                    "lastModifiedDateTime": {
                        "type": "string"
                    },
                    "roleScopeTagIds": {
                        "items": {
                            "type": "string"
                        },
                        "type": "array"
                    },
                    "templateId": {
                        "type": "string"
                    }
                },
                "required": [
                    "id",
                    "displayName",
                    "description",
                    "isAssigned",
                    "lastModifiedDateTime",
                    "templateId",
                    "roleScopeTagIds"
                ],
                "type": "object"
            },
            "type": "array"
        }
    },
    "type": "object"
}

We add a Condition to our flow. We filter on the value isAssigned. If the profile is not assigned, the value is false.
Add isAssigned to the left box, choose is equal to from the drop-down, and enter false to the right box.

Like in the first branch, add an HTTP action under True.

And this is our complete Logic Apps flow.

Which notifies on new Security baselines or security baseline profiles that are not assigned!

Thanks for reading.
And don’t forget to also read the other MEM Monitoring posts.

Be the first to comment

Leave a Reply

Your email address will not be published.


*