Remove-StaleIntuneDevices using a scheduled Azure Runbook

I recently came upon a really cool post by Josh and Sarah that explains how to clean up stale devices in Intune using the Graph API.

As I want to run this from an Azure runbook, silently, I had to modify it a little so it automatically consents to azure app permissions and logs in silently. If you’d like to use it, feel free to add it from the Azure gallery (search for Lieben) or download it yourself.

Make sure you’ve also imported the AzureAD and AzureRM modules into your automation account, and configured a credential object for the script to use.

GitLab: Remove-StaleIntuneDevicesForAzureAutomation.ps1

Technet: Remove-Stale-Intune-4b07488a

How to grant OAuth2 permissions to an Azure AD Application using PowerShell unattended / silently

You may know this button:There is no native Powershell command to grant OAuth permissions to an Azure AD Application, so I wrote a function for that. Note that this is NOT a supported way to grant permissions to an application because it does not follow the proper admin consent flow that applications normally use.

The great advantage of my method is that it can be used to grant permissions silently, AND to ‘hidden’ and/or multi-tenant applications that companies like Microsoft use for backend stuff like the Intune API. (e.g. the ‘Microsoft Intune Powershell’ multi-tenant application).

The function requires AzureAD and AzureRM modules installed!

Function Grant-OAuth2PermissionsToApp{
        [Parameter(Mandatory=$true)]$Username, #global administrator username
        [Parameter(Mandatory=$true)]$Password, #global administrator password
        [Parameter(Mandatory=$true)]$azureAppId #application ID of the azure application you wish to admin-consent to

    $secpasswd = ConvertTo-SecureString $Password -AsPlainText -Force
    $mycreds = New-Object System.Management.Automation.PSCredential ($Username, $secpasswd)
    $res = login-azurermaccount -Credential $mycreds
    $context = Get-AzureRmContext
    $tenantId = $context.Tenant.Id
    $refreshToken = @($context.TokenCache.ReadItems() | Where-Object {$_.tenantId -eq $tenantId -and $_.ExpiresOn -gt (Get-Date)})[0].RefreshToken
    $body = "grant_type=refresh_token&refresh_token=$($refreshToken)&resource=74658136-14ec-4630-ad9b-26e160ff0fc6"
    $apiToken = Invoke-RestMethod "$tenantId/oauth2/token" -Method POST -Body $body -ContentType 'application/x-www-form-urlencoded'
    $header = @{
    'Authorization' = 'Bearer ' + $apiToken.access_token
    'X-Requested-With'= 'XMLHttpRequest'
    'x-ms-client-request-id'= [guid]::NewGuid()
    'x-ms-correlation-id' = [guid]::NewGuid()}
    $url = "$azureAppId/Consent?onBehalfOfAll=true"
    Invoke-RestMethod –Uri $url –Headers $header –Method POST -ErrorAction Stop

GITLAB: Grant-OAuth2PermissionsToApp.ps1

Update 2021: improved / mfa compatible token function

Connect-AzureRMAccount requires CASE SENSITIVE input for the tenantId

Just for those poor souls googling this error:

get-azurermvm : Your Azure credentials have not been set up or have expired, please run Connect-AzureRmAccount to set
up your Azure credentials.

Or any other command after Connect-AzureRMAccount with the -tenantId switch specified.

Took me over an hour to figure out that the tenant ID is actually case sensitive as the error is confusing, the log in works fine but subsequent commands fail.

Name must be unique per owning mailbox. There’s already a request with the name

While migrating some public folders to Office 365 Groups, I kept running into issues with one of the target groups:

“De gebruiker XXX heeft al een aanvraag die in behandeling is. Verwijder de bestaande aanvraag en hervat de huidige batch of start een nieuwe batch voor deze gebruiker. –> Name must be unique per owning mailbox. T”

In english you’ll probably see “Name must be unique per owning mailbox. There’s already a request with the name “.

I figured there was a moverequest hanging / not properly cleaned up; but none to be found with get-moverequest, get-migrationuser or get-migrationbatch; all clean!

In the end, it took almost 2 weeks of patience after contacting support until the Exchange Online backend team reset a hanging job on their end. So if you google above errors and come here, check if you have double jobs, if you don’t, request support and make sure they escalate to the product team immediately.