-
Notifications
You must be signed in to change notification settings - Fork 662
Description
DESCRIPTION:
Device control rules in Microsoft Intune can only be created and managed through the Intune Admin Portal UI. There is no programmatic way to add rules to existing configurationPolicies via the Microsoft Graph API.
This prevents organizations from:
- Automating device control policy deployments
- Creating rules programmatically based on inventory or business logic
- Integrating device control with infrastructure-as-code (Terraform, PowerShell DSC, etc.)
- Bulk managing rules across multiple policies
CURRENT BEHAVIOR:
All attempts to update device control rules via Graph API fail with BadRequest errors:
- PATCH Operations: "Delta patch payload must contain @context : #$delta"
- Navigation Property Updates: "Cannot apply PATCH to navigation property 'settings' on entity type 'microsoft.management.services.api.deviceManagementConfigurationPolicy'"
- PUT Operations: Same navigation property errors
Error Examples:
POST /deviceManagement/configurationPolicies
Status: 400 Bad Request
Error: "Delta patch payload must contain @context : #$delta"
PATCH /deviceManagement/configurationPolicies/{policyId}/settings
Status: 400 Bad Request
Error: "Cannot apply PATCH to navigation property 'settings'"
EXPECTED BEHAVIOR:
Organizations should be able to:
- Create new device control rules programmatically
- Update existing rules via PATCH/PUT operations
- Manage rules through Graph API just like other configuration policies
- Automate rule deployment using standard IaC tools
USE CASE / SCENARIO:
Enable bulk deployment of USB device control rules across multiple devices based on:
- Inventory systems (device model, location, department)
- Business requirements (new security standards)
- Automated provisioning workflows
- Multi-tenant management scenarios
CURRENT WORKAROUND:
None. Rules must be created manually in the Intune Admin Portal, which:
- Doesn't scale for large organizations
- Prevents automation
- Creates compliance and consistency issues
- Blocks DevOps/IaC workflows
IMPACT:
- Organizations with thousands of devices cannot automate device control rules
- Blocks Terraform provider support (microsoft/terraform-provider-msgraph)
- Blocks PowerShell DSC support (Microsoft365DSC)
- Limits enterprise automation capabilities
- Affects organizations managing multiple tenants
RELATED ISSUES:
This is a known limitation affecting multiple organizations and tools:
-
deviceManagement/configurationPolicies - update fails microsoft/terraform-provider-msgraph#36
"deviceManagement/configurationPolicies - update fails"
deviceManagement/configurationPolicies - update fails microsoft/terraform-provider-msgraph#36 -
IntuneDeviceControlPolicyWindows10: Error, Count Of List of Setting Values 3, does not meet bound requirements. microsoft/Microsoft365DSC#5086
"IntuneDeviceControlPolicyWindows10: Cannot add device control rules"
IntuneDeviceControlPolicyWindows10: Error, Count Of List of Setting Values 3, does not meet bound requirements. microsoft/Microsoft365DSC#5086 -
IntuneExploitProtectionPolicyWindows10SettingCatalog: Cannot update exploitprotectionsettings after deployment or delete policy microsoft/Microsoft365DSC#3962
"Cannot update configurationPolicies settings"
IntuneExploitProtectionPolicyWindows10SettingCatalog: Cannot update exploitprotectionsettings after deployment or delete policy microsoft/Microsoft365DSC#3962 -
Feature Request: Support for Endpoint Security Policies microsoft/Microsoft365DSC#4229
"Feature Request: Support for Endpoint Security Policies"
Feature Request: Support for Endpoint Security Policies microsoft/Microsoft365DSC#4229
TESTING EVIDENCE:
Tested multiple approaches with /deviceManagement/configurationPolicies endpoint:
-
PATCH with full settings array - FAILED
- Error: Delta patch format required
-
PATCH with delta format (@context) - FAILED
- Error: Delta patch not supported for settings navigation property
-
PUT entire policy - FAILED
- Error: Navigation property PATCH not supported
-
POST new settings - FAILED
- Error: No POST endpoint for settings
All attempts confirmed that write operations to configurationPolicies settings are intentionally blocked at the API level.
REQUESTED CHANGES:
- Enable PATCH/PUT operations on configurationPolicies/settings navigation property
- Document the correct format for programmatic rule creation
- Provide Graph API support equivalent to what exists in the Intune UI
- Consider applying the same approach used in PR New Script to find App Protection policy unlicensed users #80 of terraform-provider-msgraph (preserving @odata.type fields for polymorphic resources)
ADDITIONAL CONTEXT:
- Organization Type: Enterprise
- Tenant Size: Mid to Large
- Use Case: Device control policy automation
- Estimated Impact: Affects automation of security policy deployment
API Endpoint being used:
POST/PATCH: https://graph.microsoft.com/beta/deviceManagement/configurationPolicies
Policy ID: (Example format)
d8659ddd-f590-4b65-bab7-24105435d83f
Current Settings Count: 2 (one group, one enabled toggle)
SUGGESTED SOLUTION:
Option 1 (Preferred): Enable full PATCH/PUT support for settings navigation property
- Allow updating existing rules via PATCH
- Allow adding new rules via POST to settings collection
- Document the correct request format
Option 2: Create dedicated endpoint for rule management
- POST /deviceManagement/configurationPolicies/{policyId}/rules
- PATCH /deviceManagement/configurationPolicies/{policyId}/rules/{ruleId}
- DELETE /deviceManagement/configurationPolicies/{policyId}/rules/{ruleId}
Option 3: Support OMA-URI XML directly in API
- Allow setting raw OMA-URI/CSP values programmatically
- Simplify the nested JSON structure for device control
VERSION INFORMATION:
- Microsoft Graph API Version: Beta
- Testing Date: January 2026
FOLLOW-UP QUESTIONS:
- Is this a design limitation or oversight?
- Are there plans to enable write operations for device control rules?
- Can early access be provided if this is in development?
- Is there an alternative supported method for programmatic rule creation?
- What permissions would be required once this is enabled?
SCRIPT:
USB Device Control Policy - View & Create Rules
Supports querying AD for SIDs and creating new USB device control rules
param(
[Parameter(HelpMessage = "Action to perform: View or Create")]
[ValidateSet("View", "Create")]
[string]$Action
)
$TenantId = ""
$ClientId = ""
$ClientSecret = ""
$policyId = "d8659ddd-f590-4b65-bab7-24105435d83f"
Color output functions
function Write-Success {
param([string]$Message)
Write-Host $Message -ForegroundColor Green
}
function Write-Warning {
param([string]$Message)
Write-Host $Message -ForegroundColor Yellow
}
function Write-Info {
param([string]$Message)
Write-Host $Message -ForegroundColor Cyan
}
function Write-Error {
param([string]$Message)
Write-Host $Message -ForegroundColor Red
}
Module installation
$requiredModules = @("Microsoft.Graph.Authentication")
foreach ($mod in $requiredModules) {
if (-not (Get-Module -ListAvailable $mod)) {
Write-Info "Installing module: $mod"
Install-Module $mod -Scope CurrentUser -Force -ErrorAction Stop
}
Import-Module $mod -ErrorAction Stop
}
Connect to Microsoft Graph
function Connect-ToMgGraph {
$SecureSecret = ConvertTo-SecureString $ClientSecret -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential($ClientId, $SecureSecret)
try {
Connect-MgGraph -ClientSecretCredential $Cred -TenantId $TenantId -NoWelcome
Write-Success "Connected to Microsoft Graph`n"
}
catch {
Write-Error "Failed to connect to Microsoft Graph: $_"
return $false
}
return $true
}
Resolve SID to name
function Resolve-SIDToName {
param([string]$SID)
try {
$objSID = New-Object System.Security.Principal.SecurityIdentifier($SID)
$objUser = $objSID.Translate([System.Security.Principal.NTAccount])
return $objUser.Value
}
catch {
return $SID
}
}
Get SID from AD for a user or computer using .NET SecurityIdentifier (same mechanism as Resolve-SIDToName)
function Get-SIDFromAD {
param(
[Parameter(Mandatory = $true)]
[string]$SamAccountName,
[Parameter(Mandatory = $false)]
[string]$ObjectType = "User",
[Parameter(Mandatory = $false)]
[string]$Domain
)
try {
# For computer objects, ensure the account name has the $ suffix
$accountNameToLookup = $SamAccountName
if ($ObjectType -eq "Computer" -and -not $SamAccountName.EndsWith('$')) {
$accountNameToLookup = "$SamAccountName`$"
}
# Use the reverse of Resolve-SIDToName approach
# Build the NTAccount name (domain\username)
$domainName = if ($Domain) { $Domain } else { $env:USERDOMAIN }
Write-Info "Current Domain: $domainName"
Write-Info "Looking up: $SamAccountName ($ObjectType)"
Write-Host ""
# Try with the specified/default domain first
Write-Info "Attempting lookup as: $domainName\$accountNameToLookup"
try {
$objNTAccount = New-Object System.Security.Principal.NTAccount($domainName, $accountNameToLookup)
$objSID = $objNTAccount.Translate([System.Security.Principal.SecurityIdentifier])
$sidValue = $objSID.Value
Write-Success "Found SID: $sidValue"
return $sidValue
}
catch {
Write-Warning "Not found in domain: $domainName"
# If it's a computer and we haven't specified a domain, try alternative methods
if ($ObjectType -eq "Computer" -and -not $Domain) {
Write-Info "Trying alternative lookup methods..."
# Try just the account name without domain qualification
Write-Info "Attempting lookup as: $accountNameToLookup (no domain)"
$objNTAccount = New-Object System.Security.Principal.NTAccount($accountNameToLookup)
$objSID = $objNTAccount.Translate([System.Security.Principal.SecurityIdentifier])
$sidValue = $objSID.Value
Write-Success "Found SID: $sidValue"
return $sidValue
}
else {
throw $_
}
}
}
catch {
Write-Error "Failed to retrieve SID for $SamAccountName ($ObjectType)"
Write-Warning "Details: $_"
Write-Warning ""
Write-Warning "Troubleshooting steps:"
Write-Warning " 1. Verify the exact computer name in AD: $SamAccountName"
Write-Warning " 2. Try specifying the domain explicitly: -ADDomain HSCDOM"
Write-Warning " 3. Try different domain formats:"
Write-Warning " - NetBIOS name (e.g., HSCDOM)"
Write-Warning " - FQDN (e.g., hscdom.local or hscdom.com)"
Write-Warning " 4. Verify network connectivity to domain controllers"
Write-Warning " 5. Check that the computer/user actually exists in AD"
Write-Warning " 6. Verify the computer name spelling and case"
return $null
}
}
Export existing rule structure for debugging
function Export-RuleStructure {
try {
Write-Info "Exporting first existing rule structure for inspection..."
Write-Host ""
$settingsUri = "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies/$policyId/settings"
$settingsResponse = Invoke-MgGraphRequest -Method GET -Uri $settingsUri
foreach ($setting in $settingsResponse.value) {
if ($setting.settingInstance['@odata.type'] -eq "#microsoft.graph.deviceManagementConfigurationGroupSettingCollectionInstance") {
$groupCollection = $setting.settingInstance['groupSettingCollectionValue']
if ($groupCollection -and $groupCollection.Count -gt 0) {
$firstRule = $groupCollection[0]
$jsonOutput = $firstRule | ConvertTo-Json -Depth 10
Write-Host "=== FIRST RULE STRUCTURE ==="
Write-Host $jsonOutput
Write-Host ""
Write-Host "=== RULE TYPE ==="
Write-Host $firstRule['@odata.type']
Write-Host ""
# Save to file for review
$exportPath = "$env:USERPROFILE\Desktop\rule-structure.json"
$jsonOutput | Out-File -FilePath $exportPath -Force
Write-Success "Full structure exported to: $exportPath"
}
break
}
}
}
catch {
Write-Error "Failed to export structure: $_"
}
}
View existing rules
function View-USBControlRules {
try {
$settingsUri = "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies/$policyId/settings"
$settingsResponse = Invoke-MgGraphRequest -Method GET -Uri $settingsUri
Write-Info "USB Device Control Policy - Block All USB Storage Devices"
Write-Info "================================================================`n"
if ($settingsResponse.value) {
foreach ($setting in $settingsResponse.value) {
if ($setting.settingInstance['@odata.type'] -eq "#microsoft.graph.deviceManagementConfigurationGroupSettingCollectionInstance") {
Write-Info "DEVICE CONTROL RULES:"
Write-Info "────────────────────────────────────────────────────────────────"
$groupCollection = $setting.settingInstance['groupSettingCollectionValue']
$ruleCount = 0
if ($groupCollection) {
foreach ($ruleGroup in $groupCollection) {
$ruleCount++
$ruleId = $null
$ruleName = $null
$computerSid = $null
$userSid = $null
$accessType = $null
$accessMask = @()
$children = $ruleGroup['children']
if ($children) {
foreach ($child in $children) {
$groupVal = $child['groupSettingCollectionValue']
if ($groupVal) {
foreach ($ruleData in $groupVal) {
$ruleChildren = $ruleData['children']
if ($ruleChildren) {
foreach ($dataChild in $ruleChildren) {
$dataDefId = $dataChild['settingDefinitionId']
$simpleValue = $dataChild['simpleSettingValue']
$groupValue = $dataChild['groupSettingCollectionValue']
if ($dataDefId -match "ruledata_id$" -and $simpleValue) {
$ruleId = $simpleValue['value']
}
elseif ($dataDefId -match "ruledata_name$" -and $simpleValue) {
$ruleName = $simpleValue['value']
}
elseif ($dataDefId -match "ruledata_entry$" -and $groupValue) {
foreach ($entry in $groupValue) {
$entryChildren = $entry['children']
if ($entryChildren) {
foreach ($entryChild in $entryChildren) {
$entryDefId = $entryChild['settingDefinitionId']
$entryChoice = $entryChild['choiceSettingValue']
$entryChoiceCollection = $entryChild['choiceSettingCollectionValue']
$simpleVal = $entryChild['simpleSettingValue']
if ($entryDefId -match "entry_type$" -and $entryChoice) {
$typeValue = $entryChoice['value']
$accessType = if ($typeValue -match "deny") { "Deny" } else { "Allow" }
}
elseif ($entryDefId -match "entry_accesmask$" -and $entryChoiceCollection) {
foreach ($maskItem in $entryChoiceCollection) {
$maskValue = $maskItem['value']
if ($maskValue -match "accesmask_1") { $accessMask += "Read" }
elseif ($maskValue -match "accesmask_2") { $accessMask += "Write" }
elseif ($maskValue -match "accesmask_4") { $accessMask += "Execute" }
}
}
elseif ($entryDefId -match "entry_sid$" -and $simpleVal) {
$sidValue = $simpleVal['value']
if ($sidValue -match "^S-1-5-") {
$userSid = $sidValue
}
}
elseif ($entryDefId -match "entry_computersid$" -and $simpleVal) {
$sidValue = $simpleVal['value']
if ($sidValue -match "^S-1-5-") {
$computerSid = $sidValue
}
}
}
}
}
}
}
}
}
}
}
}
Write-Host ""
Write-Host "Rule $($ruleCount):"
Write-Host " Name: $ruleName"
Write-Host " ID: $ruleId"
Write-Host " Access Type: $accessType"
if ($accessMask.Count -gt 0) {
Write-Host " Access Mask: $($accessMask -join ', ')"
}
Write-Host " Reusable Setting: All Removable Storage"
if ($computerSid) {
$computerName = Resolve-SIDToName -SID $computerSid
Write-Host " Computer: $computerName [$computerSid]"
}
if ($userSid) {
$userName = Resolve-SIDToName -SID $userSid
Write-Host " User: $userName [$userSid]"
}
}
}
Write-Host ""
}
if ($setting.settingInstance['@odata.type'] -eq "#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance") {
$enabledValue = $setting.settingInstance['choiceSettingValue']['value']
$isEnabled = if ($enabledValue -match "enabled_1") { "ENABLED" } else { "DISABLED" }
Write-Info "DEVICE CONTROL STATUS:"
Write-Info "────────────────────────────────────────────────────────────────"
Write-Host "Status: $isEnabled`n"
}
}
}
$policy = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies/$policyId"
Write-Info "POLICY DETAILS:"
Write-Info "────────────────────────────────────────────────────────────────"
Write-Host "Policy Name: $($policy.name)"
Write-Host "Policy ID: $($policy.id)"
Write-Host "Platform: $($policy.platforms)"
Write-Host "Created: $($policy.createdDateTime)"
Write-Host "Last Modified: $($policy.lastModifiedDateTime)`n"
}
catch {
Write-Error "Failed to retrieve rules: $_"
}
}
Create a new rule
function New-USBControlRule {
param(
[Parameter(Mandatory = $true)]
[string]$MachineName,
[Parameter(Mandatory = $false)]
[string]$UserID,
[Parameter(Mandatory = $false)]
[string]$ADDomain
)
Write-Info "Creating new USB Device Control rule...`n"
# Validate machine name format
if ($MachineName -notmatch '^[a-zA-Z0-9]+$') {
Write-Error "Invalid machine name format: $MachineName"
return $false
}
# Get machine SID
$computerSID = Get-SIDFromAD -SamAccountName $MachineName -ObjectType "Computer" -Domain $ADDomain
if (-not $computerSID) {
return $false
}
Write-Host ""
# Build rule name and get user SID if provided
$ruleName = $MachineName
$userSID = $null
if ($UserID) {
$userSID = Get-SIDFromAD -SamAccountName $UserID -ObjectType "User" -Domain $ADDomain
if (-not $userSID) {
return $false
}
Write-Host ""
$ruleName = "$MachineName`:$UserID"
}
# Generate a unique rule ID (GUID)
$ruleID = [System.Guid]::NewGuid().ToString()
Write-Info "Rule Configuration:"
Write-Info "────────────────────────────────────────────────────────────────"
Write-Host "Rule Name: $ruleName"
Write-Host "Rule ID: $ruleID"
Write-Host "Machine Name: $MachineName"
Write-Host "Machine SID: $computerSID"
if ($UserID) {
Write-Host "User ID: $UserID"
Write-Host "User SID: $userSID"
}
Write-Host "Access Type: Allow"
Write-Host "Access Mask: Read, Write, Execute"
Write-Host "Reusable Setting: All Removable Storage`n"
# Build the rule payload
$rulePayload = Build-RulePayload -RuleID $ruleID -RuleName $ruleName -ComputerSID $computerSID -UserSID $userSID
if (-not $rulePayload) {
Write-Error "Failed to build rule payload"
return $false
}
# Submit the new rule by PATCH the policy with settings
try {
Write-Info "Submitting rule to policy..."
# Fetch current settings
Write-Info "Fetching current settings..."
$settingsUri = "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies/$policyId/settings"
$settingsResponse = Invoke-MgGraphRequest -Method GET -Uri $settingsUri
# Get the device control rules setting
$dcRuleSetting = $null
foreach ($setting in $settingsResponse.value) {
if ($setting.settingInstance['@odata.type'] -eq "#microsoft.graph.deviceManagementConfigurationGroupSettingCollectionInstance") {
$dcRuleSetting = $setting
Write-Info "Found device control setting with ID: $($setting.id)"
break
}
}
if (-not $dcRuleSetting) {
Write-Error "Could not find device control rules setting"
return $false
}
# Add the new rule to the collection
$groupCollection = $dcRuleSetting.settingInstance['groupSettingCollectionValue']
if ($null -eq $groupCollection) {
$groupCollection = @()
}
$groupCollection += @($rulePayload)
# Update the setting instance with the new rules
$dcRuleSetting.settingInstance['groupSettingCollectionValue'] = $groupCollection
# Build the complete settings array
$updatedSettings = @()
foreach ($setting in $settingsResponse.value) {
if ($setting.settingInstance['@odata.type'] -eq "#microsoft.graph.deviceManagementConfigurationGroupSettingCollectionInstance") {
# Update with new rules
$setting.settingInstance['groupSettingCollectionValue'] = $groupCollection
}
$updatedSettings += $setting
}
# Build the policy update payload with settings embedded
$policyUpdatePayload = @{
settings = $updatedSettings
}
$body = ConvertTo-Json $policyUpdatePayload -Depth 50
# Debug: Show the payload being submitted
Write-Info "DEBUG: Payload being submitted:"
Write-Info "───────────────────────────────────────────────────────────────"
# Show just the new rule part for debugging
$newRuleJson = ConvertTo-Json $rulePayload -Depth 20
Write-Host $newRuleJson
Write-Host ""
$policyUri = "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies/$policyId"
Write-Info "Updating policy with embedded settings..."
Write-Info " Policy URI: $policyUri"
Write-Info " Number of settings: $($updatedSettings.Count)"
Invoke-MgGraphRequest -Method PUT -Uri $policyUri -Body $body | Out-Null
Write-Success "Rule created successfully!`n"
return $true
}
catch {
Write-Error "Failed to create rule: $_"
Write-Warning "Error details: $_"
return $false
}
}
Build rule payload structure based on actual API format from existing rules
function Build-RulePayload {
param(
[string]$RuleID,
[string]$RuleName,
[string]$ComputerSID,
[string]$UserSID
)
# Build the rule using the exact structure from actual API responses
# Root object has settingValueTemplateReference but no @odata.type
$rulePayload = @{
settingValueTemplateReference = $null
children = @(
@{
'@odata.type' = '#microsoft.graph.deviceManagementConfigurationSimpleSettingInstance'
settingDefinitionId = "ruledata_id"
settingInstanceTemplateReference = $null
simpleSettingValue = @{
'@odata.type' = '#microsoft.graph.deviceManagementConfigurationStringSettingValue'
settingValueTemplateReference = $null
value = $RuleID
}
auditRuleInformation = $null
},
@{
'@odata.type' = '#microsoft.graph.deviceManagementConfigurationSimpleSettingInstance'
settingDefinitionId = "ruledata_name"
settingInstanceTemplateReference = $null
simpleSettingValue = @{
'@odata.type' = '#microsoft.graph.deviceManagementConfigurationStringSettingValue'
settingValueTemplateReference = $null
value = $RuleName
}
auditRuleInformation = $null
},
@{
'@odata.type' = '#microsoft.graph.deviceManagementConfigurationGroupSettingCollectionInstance'
settingDefinitionId = "ruledata_entry"
settingInstanceTemplateReference = $null
auditRuleInformation = $null
groupSettingCollectionValue = @(
@{
settingValueTemplateReference = $null
children = @(
@{
'@odata.type' = '#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance'
settingDefinitionId = "entry_type"
settingInstanceTemplateReference = $null
auditRuleInformation = $null
choiceSettingValue = @{
'@odata.type' = '#microsoft.graph.deviceManagementConfigurationChoiceSettingValue'
settingValueTemplateReference = $null
value = "allow_1"
}
},
@{
'@odata.type' = '#microsoft.graph.deviceManagementConfigurationChoiceSettingCollectionInstance'
settingDefinitionId = "entry_accesmask"
settingInstanceTemplateReference = $null
auditRuleInformation = $null
choiceSettingCollectionValue = @(
@{
'@odata.type' = '#microsoft.graph.deviceManagementConfigurationChoiceSettingValue'
settingValueTemplateReference = $null
value = "accesmask_1"
},
@{
'@odata.type' = '#microsoft.graph.deviceManagementConfigurationChoiceSettingValue'
settingValueTemplateReference = $null
value = "accesmask_2"
},
@{
'@odata.type' = '#microsoft.graph.deviceManagementConfigurationChoiceSettingValue'
settingValueTemplateReference = $null
value = "accesmask_4"
}
)
},
@{
'@odata.type' = '#microsoft.graph.deviceManagementConfigurationSimpleSettingInstance'
settingDefinitionId = "entry_sid"
settingInstanceTemplateReference = $null
simpleSettingValue = @{
'@odata.type' = '#microsoft.graph.deviceManagementConfigurationStringSettingValue'
settingValueTemplateReference = $null
value = $UserSID
}
auditRuleInformation = $null
},
@{
'@odata.type' = '#microsoft.graph.deviceManagementConfigurationSimpleSettingInstance'
settingDefinitionId = "entry_computersid"
settingInstanceTemplateReference = $null
simpleSettingValue = @{
'@odata.type' = '#microsoft.graph.deviceManagementConfigurationStringSettingValue'
settingValueTemplateReference = $null
value = $ComputerSID
}
auditRuleInformation = $null
}
)
}
)
}
)
}
return $rulePayload
}
Main execution
try {
if (-not (Connect-ToMgGraph)) {
exit 1
}
if (-not $Action) {
Write-Info "USB Device Control Policy Management"
Write-Info "════════════════════════════════════════"
Write-Host "1. View existing rules"
Write-Host "2. Create new rule"
Write-Host "3. Export rule structure (for debugging)"
Write-Host ""
$choice = Read-Host "Select an option (1, 2, or 3)"
switch ($choice) {
"1" { $Action = "View" }
"2" { $Action = "Create" }
"3" { $Action = "Export" }
default {
Write-Error "Invalid selection"
exit 1
}
}
}
switch ($Action) {
"View" {
View-USBControlRules
}
"Export" {
Export-RuleStructure
}
"Create" {
Write-Info "Create New USB Device Control Rule"
Write-Info "═══════════════════════════════════"
Write-Host ""
$MachineName = Read-Host "Enter machine name (e.g., mxl12345)"
if (-not $MachineName) {
Write-Error "Machine name is required"
exit 1
}
$UserID = Read-Host "Enter user ID (optional, press Enter to skip)"
if (-not $UserID -or $UserID.Trim() -eq "") {
$UserID = $null
}
$ADDomain = Read-Host "Enter AD domain if different from default (optional, press Enter to use default)"
if (-not $ADDomain -or $ADDomain.Trim() -eq "") {
$ADDomain = $null
}
Write-Host ""
$success = New-USBControlRule -MachineName $MachineName -UserID $UserID -ADDomain $ADDomain
if (-not $success) {
exit 1
}
}
}
}
catch {
Write-Error "Unexpected error: $_"
exit 1
}
finally {
if (Get-MgContext) {
Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
}
}