Skip to main content

Using Entra ID Device Attributes for Conditional Access Exceptions

· loading ·
PowerShell Graph API Security Conditional Access
Author
Nick Benton
Principal Modern Device Management Consultant and Intune Blogger
Table of Contents

Introduction
#

Imagine you’ve spent time getting your Windows devices enrolled into Intune, they’re all getting Device Compliance policies, and you’ve finally pulled the trigger on your shiny new Conditional Access Policy that require device compliance for all your users across Windows devices, and low and behold, you’ve broken access to Microsoft 365 authenticated services from your Remote Desktop service environment, or even VDI environments.

So what next? Use the trusted location exemption in your policy for the virtual environment? What happens if the public facing address range is the same as your workstation environments or that you’d rather not blow a massive hole in security by allowing an entire IP address range free access to Microsoft 365 authenticated services? That’s not very Zero Trust of you is it.

Filtering Devices
#

Luckily within the the Conditional Access Policy configuration, there is a way to filter devices based on the device object registered with Entra ID, there are some useful native device attributes that can be used as long as your devices have registered in Entra ID (think Hybrid Entra Joined for this VDI environment).

What happens when you can’t find a suitable attribute to use that excludes your virtual environment, but still includes your end user computer workstations? The answer here are the attributes extensionAttribute1-15, which are attributes attached only to the Entra ID device object, and not synchronised, and require specific permissions to set using Graph. These seem like a good way to go.

Getting Attributes
#

For us to understand how these attributes can be updated or added, we’d probably best take a look at how we see the attributes in the first place. Let’s start with a device in Entra ID, we can see from the below where these attributes should be:

Entra ID Device

Currently this device has no attributes assigned, shock. We’ll need the Object ID in a second, so may as well grab it now.

What about using Graph? We can pull back these attributes by querying the device itself, using a GET to https://graph.microsoft.com/beta/devices/{objectId}/, so for this device the call is https://graph.microsoft.com/beta/devices/492a52d0-ec1a-40ed-8ea5-79f27aa0f8bf/.

Graph Explorer

Yup still no attributes.

Adding Attributes
#

Now that we can see where these attributes reside on the device object in Entra ID, if we’re going to be using them as a filtering option in our Conditional Access Policy, we should probably have a look at how we update them.

Pretty straight forward, as it’s the device object we’re updating, we can create, remove, or replace these attributes using a PATCH to the same Graph endpoint https://graph.microsoft.com/beta/devices/492a52d0-ec1a-40ed-8ea5-79f27aa0f8bf/ using the JSON format in the documentation and like the example below.

{
  "extensionAttributes": {
      "extensionAttribute1": "I'm an Attribute",
      "extensionAttribute2": "I'm also an Attribute"
  }
}

Punting the above to the device using Graph Explorer, we get the expected response of {} which is always pleasant, and querying the device object again with a GET, we can now see the attributes updated.

Graph Explorer New Attribute

We’re onto something here.

Updating or Removing Attributes
#

We need to be careful with our PATCH calls to the device object, as they will update the attributes, this means removal and update of existing attributes, you can see using the below example that we can change extensionAttribute1 and just plain remove extensionAttribute2.

{
  "extensionAttributes": {
      "extensionAttribute1": "CAP-00-Out",
      "extensionAttribute2": ""
  }
}

Now the device object is an attribute crisis.

Graph Explorer Updated Attribute

I’ve given the attribute a sensible value of CAP-00-Out as we can utilise the range of operators available in the Conditional Access Policy device filter for the attributes.

Filter for Devices
#

So now we have a way to essentially tag devices we want to exclude from a Conditional Access Policy, we should look at how we create a device filter, and also explore the fact we can build a naming convention with these “tags” and utilise the operators available.

You have to be careful with filtering within Conditional Access Policies, as there is some level of logic over not only supported operators, but also the attributes that use the operators, and how they are evaluated.

For our scenario, we’re going to use extensionAttributes which as of today, support the following operators Equals, NotEquals, StartsWith, NotStartsWith, EndsWith, NotEndsWith, Contains, NotContains, In, NotIn

So building a quick query in the policy, we can exclude devices where extensionAttribute1 equals CAP-00-Out.

Conditional Access Filter Equals

Or if we want to be clever and capture devices where where extensionAttribute1 ends in Out regardless of the other content.

Conditional Access Filter Ends

So there is value in coming up with good naming conventions across all areas of Entra ID and Microsoft Intune, who knew?

Filtering in Action
#

Having created a Conditional Access Policy in Report-only mode to Require device to be marked as compliant or Require Microsoft Entra hybrid joined device on Windows device platforms, with the filter to exclude devices matching device.extensionAttribute1 -eq "CAP-00-Out", we can see in the sign in logs for the user in scope of the policy, when accessing Microsoft 365 authenticated resources, that they are not subject to the policy.

Sign In Logs

Which for our situation is exactly the outcome we’re after. Now time to make this less of a manual process.

PowerShell and Graph
#

I’ll assume you’re familiar with authenticating to Graph, and this time instead of the trusty MSAL.PS module I’m using Microsoft the native commands, ikr, shocking. Now that we’re connected to Graph with the required scope options using Connect-MgGraph -Scopes 'Device.ReadWrite.All', we need to be able to pull back all the devices we want to assign an attribute to.

Getting Devices
#

Who knew that when working in non-development tenants that you’d have to consider pagination, so yeah, I had to throw something together to pull back all Entra ID device objects using the very same Graph endpoint we used earlier to look at a single device https://graph.microsoft.com/beta/devices/ and the Invoke-MgGraphRequest command.

Function Get-DeviceAAD() {

    $graphApiVersion = 'beta'
    $Resource = 'devices'

    try {
        $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource"
        $GraphResults = Invoke-MgGraphRequest -Method GET -Uri $uri

        $Results = @()
        $Results += $GraphResults.value

        $Pages = $GraphResults.'@odata.nextLink'
        while ($null -ne $Pages) {

            $Additional = Invoke-MgGraphRequest -Method GET -Uri $Pages

            if ($Pages) {
                $Pages = $Additional.'@odata.nextLink'
            }
            $Results += $Additional.value
        }
        $Results
    }
    catch {
        $exs = $Error.ErrorDetails
        $ex = $exs[0]
        Write-Host "Response content:`n$ex" -f Red
        Write-Host
        Write-Error "Request to $Uri failed with HTTP Status $($ex.Message)"
        Write-Host
        break
    }
}

For the pagination, we’re capturing the results of the web request into an array variable $Results, and looping through the @odata.nextLink data with a subsequent Graph calls, adding each result back into the same array variable until there are no more results. Simple right?

Running this will allow us to pull back all Entra ID device objects, and with the below we can present them in a table using Out-GridView.

$Devices = @(Get-DeviceAAD | Select-Object displayName, operatingSystem, manufacturer, model, id, deviceId  | Out-GridView -PassThru -Title 'Select Devices to update extension attributes...')

Allowing the selection of the devices that need their extension attributes updating, and importantly the id of the device object used to update the attribute.

Entra ID Device

Extension Attribute JSON
#

The JSON content we can build from another Out-GridView prompt, with some logic so that you only select a single extension Attribute to update, and we can pass that selection using $Attribute and a simple Read-Host to prompt for the value of the attribute itself, added to the $attributeValue variable into the JSON format needed to update the extension attributes.

$extensionAttributes = @()
for ($i = 1; $i -le 15; $i++) {
    $extensionAttributes += 'extensionAttribute' + $i
}

$Attribute = @($extensionAttributes | Out-GridView -PassThru -Title 'Select only one attribute you wish to update...')

while ($Attribute.count -gt 1) {
    Write-Host 'Only select one attribute to update...' -ForegroundColor Yellow
    $Attribute = @($extensionAttributes | Out-GridView -PassThru -Title 'Select only one attribute you wish to update...')
}

With the data required now captured, we can build the JSON that can be passed to the device object Graph endpoint to update the required data.

$JSON = @"
    {
        "extensionAttributes": {
            "$Attribute": "$attributeValue"
        }
    }
"@

Running these PowerShell sections in isolation we get a JSON output similar to the below:

{
    "extensionAttributes": {
        "extensionAttribute1": "CAP-00-Out"
    }
}

Giving us the required data and attribute value to update the device object, now we just need a function to do this.

Updating Attributes Function
#

With the device or devices now at our disposable, including the Id we used earlier in Graph Explorer, we could do with a function to update the device with the new extension attribute value, handing over the JSON content we created that is required to update the object.

Function Add-DeviceAttribute() {

    [cmdletbinding()]

    param
    (
        [parameter(Mandatory = $true)]
        $JSON,

        [parameter(Mandatory = $true)]
        $deviceID
    )

    $graphApiVersion = 'Beta'
    $Resource = "devices/$deviceID"

    try {
        Test-Json -Json $JSON
        $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
        Invoke-MgGraphRequest -Uri $uri -Method Patch -Body $JSON -ContentType 'application/json'
    }
    catch {
        $exs = $Error.ErrorDetails
        $ex = $exs[0]
        Write-Host "Response content:`n$ex" -f Red
        Write-Host
        Write-Error "Request to $Uri failed with HTTP Status $($ex.Message)"
        Write-Host
        break
    }
}

Now we can run the below passing in the device object id and the JSON content to update the device attributes.

foreach ($device in $devices){
    Add-DeviceAttribute -deviceID $device.id -JSON $JSON
}

Bulk Updating Device Attributes
#

Now we have all the component parts, we can build the full script giving us an option with Mode and options Update and Remove to choose whether we want to update attributes, or remove them.

Removing Attributes
#

We should probably run this a couple of times to show what the script can do, we’ll start with removing the data in an attribute using the below.

./Invoke-DeviceExtensionAttributes.ps1 -Mode Remove

Taking our existing device PHXNB-695630804 we used and manually added attributes to, we can clear our original data in extensionAttribute1.

Selecting the device when prompted.

Remove Device

Selecting the attribute we want to clear.

Remove Attribute

Confirming we want to continue.

Remove Warn

Now we can check the device in Entra ID and confirm that the attribute has now been cleared.

Remove Entra

Now that we’ve got a blank canvas, let’s update some device objects with a new extension attribute value.

Updating Attributes
#

We can now run the script again, but with the Update option, and start tagging multiple devices at once.

./Invoke-DeviceExtensionAttributes.ps1 -Mode Update

This time we’re asked to enter in the value of the attribute, here entering CAP-00-Out

Update Device

Selecting a handful of devices when prompted.

Update Device

Selecting the attribute we want to update.

Update Attribute

This warning section now shows how many devices, the attribute extension to be updated and the new value.

Update Warn

Confirming we want to continue, the devices have now been updated with the CAP-00-Out in extensionAttribute1.

Update Warn

Checking a device in Entra ID gives us exactly the result we want.

Update Entra

Take my word for it that the other five devices have updated too 😅.

Summary
#

This may seem like a simple script, and honestly it is, but the application of the script, to allow for tagging of extension attributes by privileged accounts only, on Entra ID device objects, that are not overwritten by directory sync for hybrid joined Windows devices, changes to device operating system version etc. for all device objects, gives us two major bits of functionality:

  • Conditional Access Policy inclusion or exclusion
  • Dynamic Security Groups

Our use case was for exclusion of Conditional Access Policies, but with the dynamic security group option, the possibilities for tagging device objects in this way and grouping them becomes almost endless, want to group devices for specific configuration in Microsoft Intune? Yup. Want to group devices for specific application deployments in Microsoft Intune? Also yup. Want to exclude groups of devices from Microsoft Intune profiles for some reason where existing Entra ID attributes or Device Filters don’t cut it, this one too.

What we have created here is at least a method for the assignment of these tags, what you can do is take this and apply it to your own requirements, hopefully making the management of devices a little more straight forward.

Related

Modernising Microsoft Intune Firewall Rule Policies
· loading
Intune Windows 10 and later PowerShell Graph API Settings Catalog Security
If you’ve ever experienced the joys of migrating Group Policy and in particular Windows Defender Firewall rules away from Group Policy to Microsoft Intune, you’ve probably encountered the Rule Migration Tool, and for now this tool has worked well. So what’s the catch?
Automating Endpoint Privilege Management Policies with PowerShell
· loading
Intune Security Windows 10 and later Settings Catalog Endpoint Privilege Management Graph API PowerShell
So you’re interested in Endpoint Privilege Management in Microsoft Intune and in you’re excitement you’ve gone and deployed a new policy to audit using the Reporting Scope options, all elevations across your managed Windows 10 and later devices. Now what?
Detailed Compliance for Non-Microsoft Antivirus Solutions
· loading
Intune Windows 10 and later Compliance Security Antivirus Graph API PowerShell
So what happens when you’re not using Windows Defender on your Windows 10 and later Microsoft Intune enrolled devices, and you’re not happy with the basic compliance checks for Third-Party Antivirus products?