Bonus! Unhealthy MEMCM Clients email report

Bonus!

As another example of how you can export data from MS Graph, below is an Azure automation Runbook you can use to generate an email report of unhealthy co-managed MEMCM clients. Specifically, it will export a list of MEMCM co-managed devices that have synced with Intune in the last 7 days but have not synced with a MEMCM management point in the last 7 days. It will then send you the list as a CSV attachment in an email.

Unhealthy MEMCM Clients runbook

Requirements

  • Your automation account must be using a managed identity

  • The managed identity must be granted the appropriate permissions to Microsoft Graph as described in this guide

  • Outlook 365 with direct send or smtp relay configured

################################################################################
## Azure automation runbook PowerShell script to export a list of unhealthy   ##
## MEMCM clients from Microsoft Intune / Endpoint Manager and send it the CSV ##
## report as an email attachment.                                             ##
################################################################################

# Set some variables
$ProgressPreference = 'SilentlyContinue'
$EmailParams = @{
    To         = 'recipient@contoso.com'
    From       = 'azureautomation@contoso.onmicrosoft.com'
    Smtpserver = 'contoso-com.mail.protection.outlook.com'
    Port       = 25
}

# Obtain an access token for MS Graph as a Managed Identity
$url = $env:IDENTITY_ENDPOINT  
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 
$headers.Add("X-IDENTITY-HEADER", $env:IDENTITY_HEADER) 
$headers.Add("Metadata", "True") 
$body = @{resource='https://graph.microsoft.com/' } 
$accessToken = (Invoke-RestMethod $url -Method 'POST' -Headers $headers -ContentType 'application/x-www-form-urlencoded' -Body $body ).access_token
$authHeader = @{
    'Authorization' = "Bearer $accessToken"
}

# Download data from MS Graph
$URI = "https://graph.microsoft.com/beta/deviceManagement/manageddevices?`$filter=StartsWith(operatingSystem,'Windows')&`$select=deviceName,enrolledDateTime,lastSyncDateTime,managementAgent,deviceEnrollmentType,userPrincipalName,model,serialNumber,userDisplayName,configurationManagerClientEnabledFeatures,configurationManagerClientHealthState"
$Response = Invoke-WebRequest -Uri $URI -Method Get -Headers $authHeader -UseBasicParsing 
$JsonResponse = $Response.Content | ConvertFrom-Json
$DeviceData = $JsonResponse.value
If ($JsonResponse.'@odata.nextLink')
{
    do {
        $URI = $JsonResponse.'@odata.nextLink'
        $Response = Invoke-WebRequest -Uri $URI -Method Get -Headers $authHeader -UseBasicParsing 
        $JsonResponse = $Response.Content | ConvertFrom-Json
        $DeviceData += $JsonResponse.value
    } until ($null -eq $JsonResponse.'@odata.nextLink')
}

# Organise the data as we want it displayed
$Devices = New-Object System.Collections.ArrayList
foreach ($item in $DeviceData)
{
    try {
        [void]$Devices.Add(
            [PSCustomObject]@{
                deviceName = $item.deviceName
                enrolledDateTime = $item.enrolledDateTime
                daysEnrolled = [math]::Round(((Get-Date) - ($item.enrolledDateTime | Get-Date -ErrorAction SilentlyContinue)).TotalDays,0)
                lastSyncDateTime = $item.lastSyncDateTime
                daysSinceLastSync = [math]::Round(((Get-Date) - ($item.lastSyncDateTime | Get-Date -ErrorAction SilentlyContinue)).TotalDays,0)
                managementAgent = $item.managementAgent
                deviceEnrollmentType = $item.deviceEnrollmentType
                userPrincipalName = $item.userPrincipalName
                model = $item.model
                serialNumber = $item.serialNumber
                userDisplayName = $item.userDisplayName
                memcmEnabledFeature_inventory = $item.configurationManagerClientEnabledFeatures.inventory
                memcmEnabledFeature_modernApps = $item.configurationManagerClientEnabledFeatures.modernApps
                memcmEnabledFeature_resourceAccess = $item.configurationManagerClientEnabledFeatures.resourceAccess
                memcmEnabledFeature_deviceConfiguration = $item.configurationManagerClientEnabledFeatures.deviceConfiguration 
                memcmEnabledFeature_compliancePolicy = $item.configurationManagerClientEnabledFeatures.compliancePolicy
                memcmEnabledFeature_windowsUpdateForBusiness = $item.configurationManagerClientEnabledFeatures.windowsUpdateForBusiness
                memcmEnabledFeature_endpointProtection = $item.configurationManagerClientEnabledFeatures.endpointProtection
                memcmEnabledFeature_officeApps = $item.configurationManagerClientEnabledFeatures.officeApps
                memcmClientHealth_state = $item.configurationManagerClientHealthState.state
                memcmClientHealth_errorCode = $item.configurationManagerClientHealthState.errorCode
                memcmClientHealth_lastSyncDateTime = $item.configurationManagerClientHealthState.lastSyncDateTime
                memcmClientHealth_daysSinceLastSync = [math]::Round(((Get-Date) - ($item.configurationManagerClientHealthState.lastSyncDateTime | Get-Date -ErrorAction SilentlyContinue)).TotalDays,0)
            }
        )
    }
    catch {} 
}

# Filter and export just the unhealthy clients - those that have talked to Intune but haven't talked to MEMCM in the last 7 days 
$UnhealthyMEMCMClients = $Devices | where {$_.memcmClientHealth_state -ne 'healthy' -and $_.daysSinceLastSync -le 7 -and $_.memcmClientHealth_daysSinceLastSync -gt 7}
$UnhealthyMEMCMClients | export-csv -Path $env:temp\UnhealthyMEMCMClients.csv -Force -NoTypeInformation 

# Send the email
Send-MailMessage @EmailParams -Subject "[Azure Automation] Unhealthy MEMCM Clients in Intune ($($UnhealthyMEMCMClients.Count))" -Attachments "$env:temp\UnhealthyMEMCMClients.csv"

Last updated