Updated April 2026 — now with iOS/iPadOS support and two deployment options
The manual grind
If you're managing macOS devices in Intune, you've probably been in this situation: Apple releases macOS 26.3, and you need to update the osMinimumVersion field in your compliance policy to require 26.1 (or whatever version makes sense for your org). You open Intune, navigate to the compliance policy, update the minimum OS version, save, and you're done.
A week later, macOS 26.4 drops. Repeat. And if you're also managing iOS/iPadOS, you're doing this dance twice.
This gets old fast. And let's be honest — we forget to do it sometimes.
What this does
This solution automatically keeps the osMinimumVersion field in your Intune compliance policies current, for both macOS and iOS/iPadOS. It pulls the latest version data from the SOFA feed (the MacAdmins community standard — updated every 6 hours directly from Apple), calculates your target minimum based on your versioning strategy, and patches the policy via the Graph API if anything has changed.
It runs on Azure Automation, costs basically nothing (well within the free tier), and once it's set up you never touch it again.
Version strategies
Before getting into setup, it's worth understanding how the versioning logic works — this is the part that makes it actually useful rather than just "latest minus one."
Track major versions — stay N major versions behind the latest release.
MACOS_VERSIONS_BELOW = 2
# Latest: macOS 26 → requires macOS 24
Pin to major version — lock to a specific major and track minor releases within it. This is the one I'd recommend for most environments.
MACOS_PIN_TO_MAJOR_VERSION = 26
MACOS_VERSIONS_BELOW = 2
# Latest 26.x is 26.7 → requires 26.5
# Ignores macOS 27.x entirely
Track minor versions — stay N minor versions behind within the same major.
MACOS_USE_MINOR_VERSIONS = True
MACOS_VERSIONS_BELOW = 2
# Latest 26.7 → requires 26.5
The same strategies apply to iOS/iPadOS using the IOS_ prefix.
Pinning to a major version is particularly useful when you want to test the next major OS on a pilot group while keeping production on the current one. You set up two compliance policies, point this automation at each, and let it handle the rest.
Two deployment options
Since v4, you can choose how to deploy this:
Separate Runbooks
Two independent runbooks — one for macOS, one for iOS/iPadOS. Each has its own schedule, variables, and diagnostics. A failure in one platform doesn't affect the other. Good if your team manages platforms independently or needs them on different update schedules.
You end up with 4 runbooks total (2 main + 2 diagnostics), but they're fully isolated.
Unified Runbook
A single runbook handles both platforms in one execution. Each platform is independently enabled/disabled via ENABLE_MACOS and ENABLE_IOS variables, so you can start with just macOS and flip iOS on later without any runbook changes.
7 variables. 2 runbooks. One scheduled job. That's the whole thing (Managed Identity, both platforms enabled).
For most environments the Unified Runbook is the right choice — simpler to manage, fewer moving parts.
Authentication: Managed Identity
The whole solution runs on Managed Identity — no client secrets, no expiry dates, no calendar reminders. The Automation Account authenticates directly to Graph as itself.
You only need one Graph permission: DeviceManagementConfiguration.ReadWrite.All. It covers both macOS and iOS policies.
The setup is:
- Enable System-Assigned Identity on your Automation Account (Identity → System assigned → On)
- Copy the Object (principal) ID
- Run this in Azure Cloud Shell:
Connect-MgGraph -Scopes "Application.Read.All","AppRoleAssignment.ReadWrite.All"
$managedIdentityId = "PASTE-YOUR-OBJECT-ID-HERE"
$graphAppId = "00000003-0000-0000-c000-000000000000"
$graphSP = Get-MgServicePrincipal -Filter "appId eq '$graphAppId'"
$permission = $graphSP.AppRoles | Where-Object { $_.Value -eq "DeviceManagementConfiguration.ReadWrite.All" }
New-MgServicePrincipalAppRoleAssignment `
-ServicePrincipalId $managedIdentityId `
-PrincipalId $managedIdentityId `
-ResourceId $graphSP.Id `
-AppRoleId $permission.IdDone. That single permission covers both platforms.
If you prefer a Service Principal (useful for running the scripts locally or in CI/CD), that's supported too — see the standalone usage guide in the repo.
Diagnostics
Each deployment option includes a diagnostics runbook that runs a 5-step pre-flight check before you schedule anything: variables, authentication, Graph API permissions, policy access, and SOFA feed connectivity. Run these first. They'll catch misconfigured variables, wrong policy IDs, and auth issues before they become a silent failure at 2 AM.
What the output looks like
{
"Success": true,
"AuthMethod": "Managed Identity",
"Duration": 6.8,
"Results": {
"macOS": {
"Platform": "macOS",
"PreviousVersion": "26.3.0",
"NewVersion": "26.4.0",
"Updated": true
},
"iOS": {
"Platform": "iOS/iPadOS",
"PreviousVersion": "18.3.0",
"NewVersion": "18.3.2",
"Updated": true
}
}
}If nothing needs updating, it says so and exits cleanly. All job history is in Azure Automation.
Get it
Everything is on GitHub — scripts, setup guides, standalone usage docs, and a migration guide if you're coming from a Service Principal setup:
SSMacAdmin/Apple_ComplianceVersion_Updater
Set it up once, forget about it.
Have fun and good luck!