Skip to main content

Account Protection Local Group Membership Management Automation with Azure Function

This automation is implemented as an Azure Function (PowerShell) that creates device-specific Account Protection policies in Microsoft Intune (via Microsoft Graph) to grant specific users local administrator rights on specific devices. It exists to provide exceptions to a global account protection policy for targeted device-user pairs.

Prerequisites

Azure Resources

This automation is deployed as an Azure Function. At minimum, you need the following resources:

  • Resource Group – to contain all components.

  • Function App (PowerShell) – hosts the scripts. Use Consumption or Premium plan.
  • Storage Account – required for Function state and triggers. Is created automatically while deploying an Azure Function App.

  • Application Insights – for telemetry and monitoring (not required but recommended).

  • Managed Identity – for secure authentication to Microsoft Graph. For permissions view next chapter.

Graph API Permissions

Your Bearer access token for the Microsoft Graph API needs at least the following scopes. If you are deploying the solution with the recommended approach using a managed identity, then grant the following permissions:

  • Device.Read.All
  • Group.ReadWrite.All

Azure Functions

 

  • Automation-LocalAdminGrant.ps1 - Main Azure Function implementation (PowerShell run.ps1)
  • Automation-LocalAdminRevoke.ps1 - Revoke function to remove the policy, group and membership

High-level Workflow

  1. Receive HTTP request (POST) with JSON body containing UserPrincipalName and DeviceName.
  2. Obtain Microsoft Graph access token — prefer supplied token in request body, fallback to the Function's Managed Identity via IMDS.
  3. Query Intune for managed devices matching DeviceName (prefix match) and select the first match.
  4. Query Azure AD (Entra ID) for the device object representing the Intune device by azureADDeviceId.
  5. Create an Entra ID security group for the device, add the device to this group, and add the device to the global exclusion group.
  6. Create or update a configuration policy (Account Protection) in Intune with the user (and optional default admin group SID) as a member.
  7. Assign the configuration policy to the device group.
  8. Return success/failure diagnostics.

Files

  • Automation-LocalAdminGrant.ps1 - Main Azure Function implementation (PowerShell run.ps1)
  • Automation-LocalAdminRevoke.ps1 - Revoke function to remove the policy, group and membership

API Contract (HTTP)

  • Endpoint: POST /api/Automation-LocalAdminGrant
  • Content-Type: application/json
  • Body parameters:
    • UserPrincipalName (string) — required
    • DeviceName (string) — required (prefix match)
    • MicrosoftEntraIDAccessToken (string) — optional; uses Managed Identity if not provided
  • Success response: HTTP 200 with body "FINAL CHECK: SUCCESS"
  • Failure responses: HTTP 400/401/500 with appropriate diagnostic messages

Example POST

$body = @{
    UserPrincipalName = 'user@example.com'
    DeviceName = 'LAPTOP-1234'
} | ConvertTo-Json

Invoke-RestMethod -Uri 'https://<func-app>.azurewebsites.net/api/Automation-LocalAdminGrant' -Method Post -Body $body -ContentType 'application/json'

Authentication (Token handling)

  • Preferred: Managed Identity (system-assigned or user-assigned) enabled on the Function App.
    • The function will request a token from the IMDS endpoint: http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://graph.microsoft.com/
  • Alternative: include an Access Token in the request body as MicrosoftEntraIDAccessToken (Bearer token). The function will use this token directly.

Required Microsoft Graph permissions (application-level)

  • Group.ReadWrite.All
  • Directory.ReadWrite.All
  • Device.ReadWrite.All
  • DeviceManagementManagedDevices.ReadWrite.All
  • DeviceManagementConfiguration.ReadWrite.All

Note: These are application permissions and require admin consent. The managed identity's service principal must be granted these permissions.

Implementation details & notable behaviours

  • CheckGroupMember accepts either a group name or GUID. It resolves names to IDs when needed and enumerates members across paginated results.
  • The policy creation uses Graph's beta/deviceManagement/configurationPolicies endpoints (beta). This is required for the specific configuration policy templates used.
  • The function may create up to three groups per device: device-specific group, device-assignment group, and an exclusion group addition (global exclusion group is pre-configured ID).
  • The function performs idempotent checks before creating groups or policies.

Error modes & troubleshooting

  • "Resource not found" when removing members: often caused by using an array object incorrectly. Ensure device lookups use the correct element index (e.g., $EntraIDDevice[0].id).
  • 401 Unauthorized: ensure managed identity has required Graph app permissions and admin consent is granted, or provide a valid Bearer token in the request.
  • Rate limiting: Graph API limits may require retry/backoff behavior — consider adding exponential backoff for production.

Deployment steps (Azure CLI)

  1. Zip deploy the Function App code (PowerShell) to your Function App.
  2. Enable Managed Identity on the Function (System-assigned):
    az functionapp identity assign -g <rg> -n <functionapp-name>
  3. Configure Graph API permissions on the function's service principal (admin consent required). You can do this via an App Registration used by the function or directly for the managed identity service principal using PowerShell/AzureAD.

Example: Grant Graph permissions via Microsoft Graph PowerShell (admin)

# Requires Microsoft.Graph PowerShell modules and admin privileges
Connect-MgGraph -Scopes 'Application.ReadWrite.OwnedBy'
$sp = Get-MgServicePrincipal -Filter "displayName eq '<function-app-name>'"
# ... assign required AppRoleAssignments or update oauth2PermissionGrants ... (admin tasks)

Example: Local testing with a token

  1. Acquire a token with required scopes (admin):
# Use Azure AD app registration with client credentials
$tenantId = '<tenant>'
$clientId = '<appclientid>'
$clientSecret = '<clientsecret>'
$body = @{
  client_id = $clientId
  scope = 'https://graph.microsoft.com/.default'
  client_secret = $clientSecret
  grant_type = 'client_credentials'
}
$token = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" -Method Post -Body $body -ContentType 'application/x-www-form-urlencoded'
$token.access_token
  1. Call the function with the token:
$body = @{UserPrincipalName='user@example.com'; DeviceName='LAPTOP-1234'; MicrosoftEntraIDAccessToken = "Bearer $($token.access_token)"} | ConvertTo-Json
Invoke-RestMethod -Method Post -Uri 'https://<func>.azurewebsites.net/api/Automation-LocalAdminGrant' -Body $body -ContentType 'application/json'

Logging & validation

  • Check Function App logs / Application Insights for detailed traces.
  • Validate groups and policies in Entra ID and Intune portals after successful runs.

Next steps / Enhancements

  • Add retries + exponential backoff for Graph calls.
  • Add richer validation and clear HTTP error codes for each failure mode.
  • Implement unit/integration tests against Graph mocks.

Deployment & Permissions (Detailed)

1) Deploy the PowerShell Azure Function

  • Create a Function App (PowerShell) in your target subscription and resource group.
  • Use ZIP deploy, VS Code deployment, or GitHub Actions to deploy the function code. Ensure the file Automation-LocalAdminGrant.ps1 is placed in the function folder and function.json is configured for HTTP trigger.

2) Enable Managed Identity

  • In the Azure Portal, go to your Function App -> Identity -> System assigned -> On -> Save.
  • Note down the object id (service principal) of the managed identity.

3) Grant Microsoft Graph application permissions to the managed identity

  • Open the Microsoft Entra admin center -> Enterprise applications.
  • Find the managed identity service principal by the Function App name.
  • Under Permissions (or API permissions) add the following Application permissions for Microsoft Graph:
    • Group.ReadWrite.All
    • Directory.ReadWrite.All
    • Device.ReadWrite.All
    • DeviceManagementManagedDevices.ReadWrite.All
    • DeviceManagementConfiguration.ReadWrite.All
  • Click Grant admin consent (requires an administrator account).
  • Alternatively, you can grant the permissions programmatically via Microsoft Graph API or Azure AD PowerShell, but admin consent is still required.

4) Verify permissions

  • Use Microsoft Graph Explorer or Microsoft.Graph PowerShell to check that the service principal can call endpoints such as GET https://graph.microsoft.com/v1.0/groups and GET https://graph.microsoft.com/beta/deviceManagement/configurationPolicies using an app-only token.

5) App Registration alternative (optional)

If you prefer not to use Managed Identity, create an App Registration and grant the same set of Application permissions to that app. Use client credentials to obtain a token and pass it to the function.

6) Function App settings

  • Review WEBSITE_RUN_FROM_PACKAGE and other deployment settings.
  • Consider enabling Application Insights for rich telemetry.

7) Monitoring and logging

  • Enable Application Insights and verify request traces and exceptions.
  • Add diagnostic logging inside the function (Write-Output, Write-Error) for key steps like token acquisition, group creation, and policy assignment.

Troubleshooting checklist

  • 401 Unauthorized: ensure admin consent was granted for application permissions to the managed identity or app registration.
  • "Resource not found" when adding/removing members: verify that the Entra ID device id is correctly extracted (e.g. $EntraIDDevice[0].id).
  • Policy creation fails: check Graph beta availability and that the service principal has DeviceManagementConfiguration.ReadWrite.All permission.
  • Throttling: add exponential backoff and log 429 responses.

Documentation generated on $(Get-Date) by automation helper.