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 existswas built to provide exceptions to a global account protection policy for targeted device-user pairs.
Prerequisites & Resources
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
-
Group.DeviceManagementManagedDevices.Read.All -
DeviceManagementConfiguration.ReadWrite.All
When you want to use the solution with multiple tenants and/or delegated permissions, you can also use an existing access token while calling the API endpoint. More on that approach below.
Azure Functions
In an Azure Function app we have to deploy two Azure Functions. One PowerShell code is to create the groups, devices, assignments, exclusions, and policies (Automation-LocalAdminGrant.ps1
). The second function is responsible for revoking the access and reverting the steps that the grant automation is doing (Automation-LocalAdminRevoke.ps1
). All the code is provided in the Github repository of LNC DOCS.
Automation-LocalAdminGrant.ps1
:-AutomationMainforAzuregrantingFunctionandimplementationcreating(PowerShelltherun.ps1)permission policies.Automation-LocalAdminRevoke.ps1
:-SecondRevokeautomationfunctionforto removereverting thepolicy,stepsgroup(removingandthemembershippermissions through policy change).
High-levelWorkflow Workflow& Architecture
ReceivechapterHTTPdescribesrequestthe(POST) with JSON body containing
workflow andUserPrincipalNameDeviceName.architecture ObtainofMicrosofttheGraphtwoaccesspartstoken(grant—&preferrevoke)suppliedoftokentheinsolution.requestThebody,automationfallbackenables per-device exceptions totheaFunction'sglobalManagedaccountIdentityprotectionviapolicyIMDS.which QueryhandlesIntunelocalforadminmanagedrightsdevicesonmatchingWindows
clients.DeviceName(prefixItmatch)doesand select the first match.Query Azure AD (Entra ID) for the device object representing the Intune deviceso byazureADDeviceId.creating Create andevice-specific Entra IDsecuritygroups,groupupdatingforIntunetheconfigurationdevice, add the device to this group,policies, andaddmanagingthemembershipdeviceinto thea global exclusion group.CreateHigh
orLevelupdateWorkflowaconfigurationpolicy(Account Protection) in Intune with the user (and optional default admin group SID) as a member.Assign the configuration policy to the device group.Return success/failure diagnostics.
This
FilesUsage & Implementation
Automation-LocalAdminGrant.ps1- Main Azure Function implementation (PowerShell run.ps1)Automation-LocalAdminRevoke.ps1- Revoke function to remove the policy, group and membership
API ContractRequest (HTTP)
using HTTP
- Endpoint:
POST /api/Automation-LocalAdminGrant
- Content-Type:
application/json
- Body parameters:
UserPrincipalName
(string) — requiredDeviceName
(string) — required (prefix match)MicrosoftEntraIDAccessToken
(string) — optional; usesManaged 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/
- The function will request a token from the IMDS endpoint:
- 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.AllDirectory.ReadWrite.AllDevice.ReadWrite.AllDeviceManagementManagedDevices.ReadWrite.AllDeviceManagementConfiguration.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)
Zip deploy the Function App code (PowerShell) to your Function App.Enable Managed Identity on the Function (System-assigned):az functionapp identity assign -g <rg> -n <functionapp-name>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
- 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
- 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 andfunction.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
andGET 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
Documentation generated on $(Get-Date) by automation helper.