Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@ This document provides a comprehensive overview of all 140 controls in the CIS M
| 5.1.3.1 | L1 | Ensure a dynamic group for guest users is created | Automated | Not Started | `entra.groups.groups` | Not Started | Collector exists but control logic not defined |
| 5.1.3.2 | L1 | Ensure users cannot create security groups | Automated | Automated | `entra.policies.authorization_policy` | Implemented | Check allowedToCreateSecurityGroups |
| 5.1.4.1 | L2 | Ensure the ability to join devices to Entra is restricted | Automated | Automated | `entra.devices.device_registration_policy` | Implemented | |
| 5.1.4.2 | L1 | Ensure the maximum number of devices per user is limited | Automated | Automated | `entra.devices.device_management_settings` | Implemented | |
| 5.1.4.3 | L1 | Ensure the GA role is not added as a local administrator during Entra join | Automated | Automated | `entra.devices.device_management_settings` | Implemented | |
| 5.1.4.4 | L1 | Ensure local administrator assignment is limited during Entra join | Automated | Automated | `entra.devices.device_management_settings` | Implemented | |
| 5.1.4.2 | L1 | Ensure the maximum number of devices per user is limited | Automated | Automated | `entra.devices.device_registration_policy` | Implemented | `userDeviceQuota > 0`; 0 treated as unlimited |
| 5.1.4.3 | L1 | Ensure the GA role is not added as a local administrator during Entra join | Automated | Automated | `entra.devices.device_registration_policy` | Implemented | `azureADJoin.localAdministratorsConfiguration.enableGlobalAdmins == false`; field may be absent on older tenants |
| 5.1.4.4 | L1 | Ensure local administrator assignment is limited during Entra join | Automated | Automated | `entra.devices.device_registration_policy` | Implemented | `azureADJoin.localAdministratorsConfiguration.registeringUsers == notAllowed` |
| 5.1.4.5 | L1 | Ensure Local Administrator Password Solution is enabled | Automated | Not Started | | Not Started | Need LAPS configuration collector |
| 5.1.4.6 | L2 | Ensure users are restricted from recovering BitLocker keys | Automated | Automated | `entra.policies.authorization_policy` | Implemented | Check allowedToReadBitlockerKeysForOwnedDevice |
| 5.1.5.1 | L2 | Ensure user consent to apps accessing company data on their behalf is not allowed | Automated | Automated | `entra.policies.authorization_policy` | Implemented | |
Expand Down
24 changes: 9 additions & 15 deletions engine/collectors/entra/devices/device_registration_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,20 @@


class DeviceRegistrationPolicyDataCollector(BaseDataCollector):
"""Collects device registration policy for CIS compliance evaluation.

This collector retrieves device join settings, LAPS configuration,
and local admin assignment settings for compliance evaluation.
"""
"""Collects device registration policy for CIS compliance evaluation."""

async def collect(self, client: GraphClient) -> dict[str, Any]:
"""Collect device registration policy data.

Returns:
Dict containing:
- device_registration_policy: The device registration policy
- azure_ad_join_settings: Azure AD join configuration
- local_admin_settings: Local admin assignment settings
- laps_settings: LAPS configuration
Dict containing device join settings, quota, local admin config, and LAPS state.
"""
# Get device registration policy
policy = await client.get("/policies/deviceRegistrationPolicy", beta=True)

# Extract key settings
azure_ad_join = policy.get("azureADJoin", {})
azure_ad_registration = policy.get("azureADRegistration", {})
local_admin_password = policy.get("localAdminPassword", {})
azure_ad_join = policy.get("azureADJoin", {}) or {}
azure_ad_registration = policy.get("azureADRegistration", {}) or {}
local_admin_password = policy.get("localAdminPassword", {}) or {}
local_admins_config = azure_ad_join.get("localAdministratorsConfiguration", {}) or {}

return {
"device_registration_policy": policy,
Expand All @@ -51,4 +42,7 @@ async def collect(self, client: GraphClient) -> dict[str, Any]:
"laps_enabled": local_admin_password.get("isEnabled"),
"user_device_quota": policy.get("userDeviceQuota"),
"multi_factor_auth_configuration": policy.get("multiFactorAuthConfiguration"),
"local_admins_config": local_admins_config,
"global_admins_enabled_on_join": local_admins_config.get("enableGlobalAdmins"),
"registering_users_local_admin": local_admins_config.get("registeringUsers"),
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# METADATA
# title: Ensure the maximum number of devices per user is limited
# description: |
# Setting an upper bound on device registrations per user reduces the attack
# surface from compromised accounts and limits lateral movement via registered
# devices. CIS recommends setting this to 5.
# related_resources:
# - ref: https://www.cisecurity.org/benchmark/microsoft_365
# description: CIS Microsoft 365 Foundations Benchmark
# custom:
# control_id: CIS-5.1.4.2
# framework: cis
# benchmark: microsoft-365-foundations
# version: v6.0.0
# severity: medium
# service: EntraID
# requires_permissions:
# - Policy.Read.DeviceConfiguration

package cis.microsoft_365_foundations.v6_0_0.control_5_1_4_2

import rego.v1

default result := {
"compliant": false,
"message": "Evaluation failed: unable to retrieve device registration policy",
"details": {},
}

compliant := true if {
Comment thread
williamywccc marked this conversation as resolved.
input.user_device_quota != null
input.user_device_quota > 0
} else := false

result := output if {
quota := input.user_device_quota
output := {
"compliant": compliant,
"message": build_message(quota),
"affected_resources": [],
"details": {"user_device_quota": quota},
}
}

build_message(q) := sprintf("Device registration quota is set to %d", [q]) if { q > 0 }
build_message(0) := "Device registration quota is set to unlimited"
build_message(null) := "Device registration quota is not configured"
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# METADATA
# title: Ensure the GA role is not added as a local administrator during Entra join
# description: |
# When a device joins Entra ID, automatically granting the Global Administrator
# role local admin rights on that machine creates unnecessary privilege exposure.
# The GA role should not be part of the local administrators configuration for
# joined devices.
# related_resources:
# - ref: https://www.cisecurity.org/benchmark/microsoft_365
# description: CIS Microsoft 365 Foundations Benchmark
# custom:
# control_id: CIS-5.1.4.3
# framework: cis
# benchmark: microsoft-365-foundations
# version: v6.0.0
# severity: medium
# service: EntraID
# requires_permissions:
# - Policy.Read.DeviceConfiguration

package cis.microsoft_365_foundations.v6_0_0.control_5_1_4_3

import rego.v1

default result := {
"compliant": false,
"message": "Evaluation failed: unable to retrieve device registration policy",
"details": {},
}

compliant := true if {
input.global_admins_enabled_on_join == false
} else := false

result := output if {
ga_enabled := input.global_admins_enabled_on_join
output := {
"compliant": compliant,
"message": build_message(ga_enabled),
"affected_resources": [],
"details": {
"global_admins_enabled_on_join": ga_enabled,
"local_admins_config": object.get(input, "local_admins_config", null),
},
}
}

build_message(false) := "Global Administrator role is not assigned local admin rights on Entra-joined devices"
build_message(null) := "Unable to determine whether GA role is granted local admin rights on join; azureADJoin.localAdministratorsConfiguration not returned by API"
build_message(true) := "Global Administrator role is configured as a local administrator on Entra-joined devices"
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# METADATA
# title: Ensure local administrator assignment is limited during Entra join
# description: |
# By default, the user registering a device may be granted local administrator
# rights on that device. Restricting this prevents standard users from gaining
# local admin access simply by joining a machine to Entra ID.
# related_resources:
# - ref: https://www.cisecurity.org/benchmark/microsoft_365
# description: CIS Microsoft 365 Foundations Benchmark
# custom:
# control_id: CIS-5.1.4.4
# framework: cis
# benchmark: microsoft-365-foundations
# version: v6.0.0
# severity: medium
# service: EntraID
# requires_permissions:
# - Policy.Read.DeviceConfiguration

package cis.microsoft_365_foundations.v6_0_0.control_5_1_4_4

import rego.v1

default result := {
"compliant": false,
"message": "Evaluation failed: unable to retrieve device registration policy",
"details": {},
}

compliant := true if {
input.registering_users_local_admin != null
lower(input.registering_users_local_admin) == "notallowed"
} else := false

result := output if {
reg_users := input.registering_users_local_admin
output := {
"compliant": compliant,
"message": build_message(reg_users),
"affected_resources": [],
"details": {
"registering_users_local_admin": reg_users,
"local_admins_config": object.get(input, "local_admins_config", null),
},
}
}

build_message(null) := "Unable to determine local admin assignment for registering users; azureADJoin.localAdministratorsConfiguration not returned by API"
build_message(val) := "Registering users are not granted local administrator rights on Entra-joined devices" if { lower(val) == "notallowed" }
build_message(val) := sprintf("Registering users are granted local admin rights on join (registeringUsers=%s)", [val])
Comment thread
williamywccc marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
Expand Up @@ -766,11 +766,11 @@
"level": "L1",
"is_manual": false,
"benchmark_audit_type": "Automated",
"automation_status": "not_started",
"data_collector_id": "entra.devices.device_management_settings",
"policy_file": null,
"requires_permissions": ["Policy.Read.All"],
"notes": null
"automation_status": "ready",
"data_collector_id": "entra.devices.device_registration_policy",
"policy_file": "5.1.4.2_limit_device_registration_quota.rego",
"requires_permissions": ["Policy.Read.DeviceConfiguration"],
"notes": "Compliant when userDeviceQuota > 0; value of 0 treated as unlimited. CIS recommends setting this to 5."
Comment thread
williamywccc marked this conversation as resolved.
Outdated
},
{
"control_id": "5.1.4.3",
Expand All @@ -781,11 +781,11 @@
"level": "L1",
"is_manual": false,
"benchmark_audit_type": "Automated",
"automation_status": "not_started",
"data_collector_id": "entra.devices.device_management_settings",
"policy_file": null,
"requires_permissions": ["Policy.Read.All"],
"notes": null
"automation_status": "ready",
"data_collector_id": "entra.devices.device_registration_policy",
"policy_file": "5.1.4.3_ga_not_local_admin_on_join.rego",
"requires_permissions": ["Policy.Read.DeviceConfiguration"],
"notes": "Uses azureADJoin.localAdministratorsConfiguration.enableGlobalAdmins from /policies/deviceRegistrationPolicy (beta). Field may be absent on older tenants; policy returns non-compliant with an explanatory message in that case."
},
{
"control_id": "5.1.4.4",
Expand All @@ -796,11 +796,11 @@
"level": "L1",
"is_manual": false,
"benchmark_audit_type": "Automated",
"automation_status": "not_started",
"data_collector_id": "entra.devices.device_management_settings",
"policy_file": null,
"requires_permissions": ["Policy.Read.All"],
"notes": null
"automation_status": "ready",
"data_collector_id": "entra.devices.device_registration_policy",
"policy_file": "5.1.4.4_limit_local_admin_on_join.rego",
"requires_permissions": ["Policy.Read.DeviceConfiguration"],
"notes": "Uses azureADJoin.localAdministratorsConfiguration.registeringUsers from /policies/deviceRegistrationPolicy (beta). Compliant when value is notAllowed."
},
{
"control_id": "5.1.4.5",
Expand Down
Loading