Zero Trust Common Identity and Device Access Protection

post-thumb

This is the 3rd step in the series Zero to Zero Trust in an automated way. In the previous steps, we configured an emergency account and protected the account with conditional access policies. The conditional access policies control the log-in process. In this step, we configure access policies for other users and devices.

deploy-identity-solution-overview

This blog post is part of the Zero to Zero Trust series
Table Of Contents

Introduction

This blog post focuses on the red boxes.

zero-trust-overview

All blogs related to the Zero to Zero Trust journey are listed in the Zero to Zero Trust overview.
All blogs related to level 1 Zero Trust are listed in the Zero Trust Level 1 overview.

The full Zero Trust plan is available here .

Prerequisites

Before using the Zero Trust identity and device access policies that are recommended, your tenant needs to meet prerequisites. The full prerequisites list is stored here .
Considering the list, the Configure name locations and Multi Factory Authentication are in place already. From the list, I’m focussing now on Azure AD password protection and Identity protection.

Authentication

Some prerequisites are part of the Azure AD that has no Graph API or Azure Management API endpoint. To configure these parts, we need the Azure AD backend API main.iam.ad.ext.azure.com

Earlier I wrote a post on how to authenticate to the backend API. You can find that post here . I use the header used in the code that I picked from this part

Add custom banned password list

Microsoft has a global banned password list that is enabled for every user automatically. You don’t have to enable a feature or something. However this is a long list that avoids creating weak passwords, there could be a situation where you want to add your passwords as well. In this example, I configure a small additional banned password list.

NOTE: The banned password needs to have a min length of 4 characters and a max length of 16 characters.

$passwordPolicyUrl = "https://main.iam.ad.ext.azure.com/api/AuthenticationMethods/PasswordPolicy"
$body = @{
  lockoutThreshold = 10
  lockoutDurationInSeconds = 60
  enforceCustomBannedPasswords = $true
  customBannedPasswords = @(
    "anotherweakpwd1"
    "anotherweakpwd2"
    "anotherweakpwd3"
    )
  enableBannedPasswordCheckOnPremises = $true
  bannedPasswordCheckOnPremisesMode = 1
} | ConvertTo-Json
$params = @{
    uri = $passwordPolicyUrl
    method = "POST"
    headers = $headers
    body = $body
}
Invoke-RestMethod @params

password-policy

For more information about password policies, check the documentation about the custom banned password list .

Identity protection

Most of the identity protection part is handled by conditional access policies. Check my blog about securing privileged accounts automated . But more settings should be checked under the Azure AD - Security - Entra ID protection blade. Users at risk detected alerts and Weekly digest, which are default enabled by Microsoft could be disabled somehow. The commands below enable risk detection events and weekly digest notifications.

We need to authenticate to the Graph API with the correct scope. I almost use the same token request where I changed the resource and scope.

First, I request the current state. This is to get all current users that are on the notification list.
Global Administrators are on the list by default. The first 20 identities on the list get a notification (per role).

Needed permissions: IdentityRiskEvent.ReadWrite.All

$uri =  "https://graph.microsoft.com/beta/identityProtection/settings/notifications"
$currentState = Invoke-WebRequest -Headers $graphHeader -Uri $uri -Method GET
$currentState.notificationRecipients.Foreach({
  $_.isRiskyUsersAlertsRecipient = $true
  $_.isWeeklyDigestRecipient=$true

  # Remove the properties that are not needed
  $_.PSObject.Properties.Remove('DisplayName')
  $_.PSObject.Properties.Remove('email')
  $_.PSObject.Properties.Remove('roles')
})
$currentState.isWeeklyDigestEnabled = $true
$currentState.minRiskLevel = "Medium"
$body = $currentState | ConvertTo-Json -Depth 5
Invoke-WebRequest -Headers $graphHeader -Uri $uri -Method PATCH -Body  $body -ContentType "application/json"

notifications

More information about configuring notifications can be found here

Conditional access policies

As mentioned before, most of the identity protection part is handled by conditional access policies. Check my blog about securing privileged accounts automated . In the blog post, I configure the conditional access policies for the emergency account. In this part, we configure the conditional access policies for all users and make sure they only log in from compliant devices.

Considering the image below, we need to configure the red-circled conditional access policies.

zero-trust-policyoverview

The purple-circled conditional access policies are already configured in the previous blog.

The policies are stored on my GitHub

To enroll the normal user conditional access policies please read the import Zero Trust conditional access policies automated part

Run the following command to import the conditional access policies for normal users. NOTE: The state in the file is set to report only. Remove the state line or enable the policies manually.

$files = Get-ChildItem -Path './Identity/ZeroTrust/CommonIdentityDeviceProtection' -Filter *.json
foreach ($file in $files) {
    $caPolUrl = "https://graph.microsoft.com/beta/identity/conditionalAccess/policies"
    $params = @{
        uri = $caPolUrl
        method = "POST"
        headers = $graphHeader
        body = get-content $file
    }
    Invoke-RestMethod @params
}

created-policies

NOTE: The policies I provided are just a subset of a wide range of policies. I would suggest taking a look at Kenneth van Surksum his website to find out a wide range of conditional access policies. He explains the scenarios where to use them.

Device protection

This is the part where Mircosoft Intune comes in. In the upcoming steps, we will configure the following settings:

  • Require device compliance
  • Require managed devices

Before enabling these settings, we need to make sure that the devices are compliant. To get the compliance status, we need to configure the compliance policies in Intune.

Filters

Because Windows 10 and Windows 11 both are under the Windows 10 and later category, we need to create a filter to find the Windows 11 devices. This is because Windows 10 and Windows 11 have different build numbers to check on later in the compliance policies.
It is recommended to use filters above dynamic groups to assign compliance policies in Intune. This way, you can assign the correct compliance policy to the correct device type. In this example, I create a filter for Windows 11. All filters are stored on my GitHub

{
    "displayName" : "Windows 11",
    "description" : "This filter finds all Windows 11 machines",
    "platform" : "windows10AndLater",
    "rule" : "(device.osVersion -contains \"10.0.22000\") or (device.osVersion -contains \"10.0.22621\") and (device.model -ne \"Virtual Machine\")"
} 

The rule is needed to find the Windows 11 devices. In later steps, we will use the filter to assign the compliance policy to the correct devices. For Windows 10 and Windows 11, the configuration profile is different.

For Windows 10, the rule is (device.osVersion -contains \"10.0.19044\") or (device.osVersion -contains \"10.0.19045\").

Create the filter with the following command:

$filterUrl = "https://graph.microsoft.com/beta/deviceManagement/assignmentFilters"
$filterParams = @{
    URI     = $filterUrl 
    Method  = "POST"
    Headers = $authHeader
    Body = Get-Content .\filters.windows11.json
}
Invoke-RestMethod @filterParams

For more information about creating filters automated, check my blog post about Intune filters explained create automated and assignments

The build number overview can be found here

Configure compliance policies

It is recommended to create a compliance policy for each device type. In this example, I create a compliance policy for Windows 11. The difference between them is the OS version build numbers. The policies are stored on my GitHub

Creating and assigning compliance policies (with filters) is a two-step process. First, we need to create a compliance policy. Second, we need to assign the compliance policy to the correct filters.
The whole process is explained in my blog post about Intune compliance policies explained create automated

In basics we create a compliance policy with the following command:

$compliancyUrl = "https://graph.microsoft.com/beta/deviceManagement/deviceCompliancePolicies/"
$filterParams = @{
    URI     = $compliancyUrl 
    Method  = "POST"
    Headers = $graphHeader
    Body = Get-Content .\compliance.windows11.json
}
$policy = Invoke-RestMethod @filterParams
$policy

Save the output into a variable. This variable is needed to assign the compliance policy to the correct filters.

Assign compliance policy to filter

In the next step, we request the Windows 11 filter. This filter is needed to assign the compliance policy to the correct devices.

$filterUrl = "https://graph.microsoft.com/beta/deviceManagement/assignmentFilters"
$filterParams = @{
    URI     = $filterUrl 
    Method  = "GET"
    Headers = $graphHeader
}
$filters = Invoke-RestMethod @filterParams
$win11Filter = $filters.value | Where-Object {$_.DisplayName -eq "Windows 11"}

win11-filter

In the last step, we assign the compliance policy to the correct filter. This is done with the following command:

$assignBody = @{
  assignments = @(
    @{
      "target" = @{
        "@odata.type"                              = "#microsoft.graph.allDevicesAssignmentTarget"
        deviceAndAppManagementAssignmentFilterId   = $win11Filter.id
        deviceAndAppManagementAssignmentFilterType = "include"
      }
    }
  )
} | ConvertTo-Json -Depth 5
$compliancyUrl = "https://graph.microsoft.com/beta/deviceManagement/deviceCompliancePolicies/{0}/assign" -f $policy.id
$filterParams = @{
  URI     = $compliancyUrl 
  Method  = "POST"
  Headers = $graphHeader
  Body    = $assignBody
}
Invoke-RestMethod @filterParams

This step must be repeated for every compliance policy. If no filters are used, then skip the GET filter step and remove the FilterId and FilterType lines in the POST body.

Configure configuration profiles

Some settings in compliance policies need a configuration profile to configure a specific setting on the device. Take Bitlocker for example. BitLocker must be installed on the device to get compliant. The configuration profile handles the Bitlocker installation and configures common security settings.

windows-protection-settings

In the example below, I create a configuration profile for all Windows devices. All configuration profiles are stored on my GitHub

$compliancyUrl = "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies"
$filterParams = @{
  URI     = $compliancyUrl 
  Method  = "POST"
  Headers = $graphHeader
  Body    = Get-Content .\windows.endpointprotection.json
}
$result = Invoke-RestMethod @filterParams

After creation, we need to assign the configuration profile to the correct compliance policy. Assigning configuration profiles works the same as assigning compliance profiles. Use the command above to assign the configuration profile to the correct compliance policy.

new-windows-policy

OS Update policies

In this part of the blog post, I will explain how to configure OS update policies. Planning updates is a complex topic and it is recommended to use several update rings instead of updating all devices at once. I will not go into detail about the planning. I will only explain how to configure the OS update policies.

$updateParams = @{
  URI     = "https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations"
  Method  = "POST"
  Headers = $graphHeader
  Body    = Get-Content .\update.windows.json
}
Invoke-RestMethod @updateParams

Again we need to assign the OS update policy to the correct devices. In the case of Windows make sure you select the correct filters as discussed in the filters section.

The OS update policies are stored on my GitHub

Disable default compliancy

Considering Zero Trust, we need to disable the feature below. This feature is enabled by default and it sets all devices to a compliant state if no policy is assigned. disable-default-compliant

The most ironic part of this is the name of the setting. It is called secureByDefault which is default set to false. In the portal, the false setting represents the compliant state and sets all devices to a compliant state if no policy is assigned.

To disable this feature, we need to create a configuration profile with the following command:

$settingsBody = @{
  settings = @{
    secureByDefault = $true
  }
} | ConvertTo-Json
$secureByDefault = @{
  URI     = "https://graph.microsoft.com/beta/deviceManagement"
  Method  = "PATCH"
  Headers = $graphHeader
  Body    = $settingsBody
}
Invoke-RestMethod @secureByDefault

Wrap Up

In this blog post, I explained how to add extra security by configuring a banned password list into Azure AD. The next step was configuring risk and health alerts. However these are enabled by default, it is good to be sure. Thereafter the ’normal’ user conditional access policies were deployed automated. The next part of the blog focus on device protection in Intune. I showed an example of configuring filters (for assignments) and creating compliance- and configuration policies for Windows 11 devices with Intune.

At last, I explained how to disable the default compliance setting in Intune automated. This setting is enabled by default and it sets all devices to a compliant state if no policy is assigned.

Thank you for reading my blog zero trust common identity and device access protection.
I hope you got a bit inspired. Enjoy your day and happy automating 👋

comments powered by Disqus