From 4e4e54078a90ca7ebe38e1b0319469f63530c156 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Dec 2025 09:51:31 +0000 Subject: [PATCH 1/4] Initial plan From 68ab35691a6188242fce08168ceadffe37a01fba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Dec 2025 09:56:10 +0000 Subject: [PATCH 2/4] Add endpoint to list outdated ActiveSync devices (version < 16.1) Co-authored-by: Zacgoose <107489668+Zacgoose@users.noreply.github.com> --- .../Invoke-ListOutdatedActiveSyncDevices.ps1 | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Reports/Invoke-ListOutdatedActiveSyncDevices.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Reports/Invoke-ListOutdatedActiveSyncDevices.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Reports/Invoke-ListOutdatedActiveSyncDevices.ps1 new file mode 100644 index 000000000000..75c1bdc105a3 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Reports/Invoke-ListOutdatedActiveSyncDevices.ps1 @@ -0,0 +1,51 @@ +function Invoke-ListOutdatedActiveSyncDevices { + <# + .FUNCTIONALITY + Entrypoint + .ROLE + Exchange.Mailbox.Read + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + # Interact with query parameters or the body of the request. + $TenantFilter = $Request.Query.TenantFilter + + try { + # Get all mobile devices for the tenant + $AllDevices = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Get-MobileDevice' + + # Filter for outdated ActiveSync devices (version < 16.1) + # ActiveSync 16.1 was released in June 2016 and will be the minimum required version starting March 1, 2026 + $MinimumVersion = [version]'16.1' + + $OutdatedDevices = $AllDevices | Where-Object { + # Check if device is using ActiveSync/EAS + ($_.ClientType -eq 'EAS' -or $_.ClientType -match 'ActiveSync') -and + # Ensure ClientVersion exists + $_.ClientVersion -and + # Check if version is less than 16.1 + ([version]$_.ClientVersion -lt $MinimumVersion) + } | Sort-Object UserDisplayName | Select-Object @{ Name = 'userDisplayName'; Expression = { $_.UserDisplayName } }, + @{ Name = 'userPrincipalName'; Expression = { $_.UserPrincipalName } }, + @{ Name = 'deviceId'; Expression = { $_.DeviceId } }, + @{ Name = 'deviceModel'; Expression = { $_.DeviceModel } }, + @{ Name = 'clientType'; Expression = { $_.ClientType } }, + @{ Name = 'clientVersion'; Expression = { $_.ClientVersion } }, + @{ Name = 'deviceOS'; Expression = { $_.DeviceOS } }, + @{ Name = 'deviceFriendlyName'; Expression = { if ([string]::IsNullOrEmpty($_.DeviceFriendlyName)) { 'Unknown' } else { $_.DeviceFriendlyName } } }, + @{ Name = 'firstSyncTime'; Expression = { $_.FirstSyncTime } }, + @{ Name = 'lastSuccessSync'; Expression = { $_.LastSuccessSync } } + + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $StatusCode = [HttpStatusCode]::Forbidden + $OutdatedDevices = $ErrorMessage + } + + return ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($OutdatedDevices) + }) +} From c806154bca2387c1f6c21b419eb809b11b1e3449 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Dec 2025 09:57:33 +0000 Subject: [PATCH 3/4] Add server-side filtering for better performance and fix version parsing validation Co-authored-by: Zacgoose <107489668+Zacgoose@users.noreply.github.com> --- .../Invoke-ListOutdatedActiveSyncDevices.ps1 | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Reports/Invoke-ListOutdatedActiveSyncDevices.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Reports/Invoke-ListOutdatedActiveSyncDevices.ps1 index 75c1bdc105a3..bf6c598daa32 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Reports/Invoke-ListOutdatedActiveSyncDevices.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Reports/Invoke-ListOutdatedActiveSyncDevices.ps1 @@ -12,20 +12,30 @@ function Invoke-ListOutdatedActiveSyncDevices { $TenantFilter = $Request.Query.TenantFilter try { - # Get all mobile devices for the tenant - $AllDevices = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Get-MobileDevice' - - # Filter for outdated ActiveSync devices (version < 16.1) + # Filter for outdated ActiveSync devices (version < 16.1) using server-side filtering # ActiveSync 16.1 was released in June 2016 and will be the minimum required version starting March 1, 2026 + # Using OPATH syntax to filter on the Exchange server for better performance + $Filter = "(ClientType -eq 'EAS' -or ClientType -like '*ActiveSync*')" + + # Get mobile devices with server-side filter + $AllDevices = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Get-MobileDevice' -cmdParams @{Filter = $Filter} + $MinimumVersion = [version]'16.1' + # Client-side filtering for version comparison (as version comparison is not supported in OPATH) $OutdatedDevices = $AllDevices | Where-Object { - # Check if device is using ActiveSync/EAS - ($_.ClientType -eq 'EAS' -or $_.ClientType -match 'ActiveSync') -and - # Ensure ClientVersion exists - $_.ClientVersion -and - # Check if version is less than 16.1 - ([version]$_.ClientVersion -lt $MinimumVersion) + # Ensure ClientVersion exists and can be parsed + if (-not $_.ClientVersion) { + return $false + } + # Safely parse and check if version is less than 16.1 + try { + $deviceVersion = [version]$_.ClientVersion + return ($deviceVersion -lt $MinimumVersion) + } catch { + # If version parsing fails, exclude the device + return $false + } } | Sort-Object UserDisplayName | Select-Object @{ Name = 'userDisplayName'; Expression = { $_.UserDisplayName } }, @{ Name = 'userPrincipalName'; Expression = { $_.UserPrincipalName } }, @{ Name = 'deviceId'; Expression = { $_.DeviceId } }, From 627cff5dbe4759859066b321e6ba4d98ae05b159 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Dec 2025 10:02:13 +0000 Subject: [PATCH 4/4] Use -ActiveSync switch instead of custom OPATH filter for better simplicity Co-authored-by: Zacgoose <107489668+Zacgoose@users.noreply.github.com> --- .../Reports/Invoke-ListOutdatedActiveSyncDevices.ps1 | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Reports/Invoke-ListOutdatedActiveSyncDevices.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Reports/Invoke-ListOutdatedActiveSyncDevices.ps1 index bf6c598daa32..e0bf7019a3e7 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Reports/Invoke-ListOutdatedActiveSyncDevices.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Reports/Invoke-ListOutdatedActiveSyncDevices.ps1 @@ -12,13 +12,12 @@ function Invoke-ListOutdatedActiveSyncDevices { $TenantFilter = $Request.Query.TenantFilter try { - # Filter for outdated ActiveSync devices (version < 16.1) using server-side filtering + # Filter for outdated ActiveSync devices (version < 16.1) using the ActiveSync switch # ActiveSync 16.1 was released in June 2016 and will be the minimum required version starting March 1, 2026 - # Using OPATH syntax to filter on the Exchange server for better performance - $Filter = "(ClientType -eq 'EAS' -or ClientType -like '*ActiveSync*')" + # Using the -ActiveSync switch to filter on the Exchange server for better performance - # Get mobile devices with server-side filter - $AllDevices = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Get-MobileDevice' -cmdParams @{Filter = $Filter} + # Get mobile devices with ActiveSync filter + $AllDevices = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Get-MobileDevice' -cmdParams @{ActiveSync = $true} $MinimumVersion = [version]'16.1'