WWDC Quick Look 💓 By SwiftGGTeam
Adopt declarative device management

Adopt declarative device management

观看原视频

Highlight

Apple 设备管理团队的 Cyrus Daboo 全面介绍了声明式设备管理(Declarative Device Management)在 2022 年的扩展。去年 WWDC 21 首次引入时仅支持 iOS 的用户注册,今年扩展到所有 MDM 注册类型、所有平台(iOS、macOS、tvOS),并大幅增强了状态报告和谓词系统。


核心内容

移动设备管理(MDM)服务端过去经常要轮询设备。

一个典型例子是密码策略。服务端先下发密码策略,再不断查询设备,等待用户把密码改到合规状态。只有等到下一次轮询确认合规,服务端才敢下发 Wi-Fi 或 VPN 这类敏感配置。

声明式设备管理(Declarative Device Management)把这段业务逻辑放到设备上。设备根据自己的状态重新计算激活条件,主动上报变化,服务端变成更轻量的反应式系统。(00:37

WWDC22 的更新集中在三件事:扩大平台和注册类型覆盖范围,增加状态报告项目,增强谓词(predicate)系统。(03:46

详细内容

覆盖范围扩展到所有 MDM 平台

04:02)声明式设备管理在 WWDC21 首次推出时,只支持 iOS 的用户注册。2022 年,它扩展到 MDM 支持的每一种注册类型:自动设备注册、基于描述文件的注册,以及基于描述文件和账户的用户注册。

它也来到 Shared iPad、macOS Ventura 和 tvOS 16。只要操作系统支持,同一套 declarations 和 status 模型可以用于 iOS、macOS 和 tvOS。

macOS 和 Shared iPad 各有 device channel 和 user channel。要在某个通道使用声明式管理,服务端必须在对应通道发送 DeclarativeManagement 命令。状态报告也按通道分别生成,服务端需要分别监听。

macOS / Shared iPad
├─ device channel  -> send DeclarativeManagement -> monitor device status report
└─ user channel    -> send DeclarativeManagement -> monitor user status report

关键点:

  • device channel 管理设备级状态。
  • user channel 管理特定用户的状态。
  • DeclarativeManagement 要按通道分别启用。
  • status report 也按通道分别产生,不能只监听其中一个。

密码状态让 Wi-Fi 配置自动激活

07:41)这场 session 增加了两个密码相关状态项:passcode.is-compliantpasscode.is-present。它们是布尔值,对应传统 MDM 查询中能拿到的密码状态。

{
  "management": {
    "client-capabilities": {
      "supported-payloads": {
        "status-items": [
          "passcode.is-compliant",
          "passcode.is-present"
        ]
      }
    }
  }
}

关键点:

  • management.client-capabilities 描述设备支持哪些声明式能力。
  • supported-payloads.status-items 列出可订阅的状态项。
  • passcode.is-compliant 表示当前密码是否符合 MDM profiles 或 configurations 下发的所有密码策略。
  • passcode.is-present 表示设备是否已有密码。

08:05)Apple 给出的场景是安全网络配置。服务端可以同时发送强密码策略和 Wi-Fi 配置,把 Wi-Fi 配置绑定到一个依赖 passcode.is-compliant 的 activation predicate。

开始时,密码通常还不合规,谓词为 false,Wi-Fi 不会激活。用户改好密码后,设备重新计算 activations,谓词变成 true,Wi-Fi 自动激活。这个过程不需要服务端轮询,甚至可以在设备暂时离线时完成。

send passcode policy configuration
send Wi-Fi configuration with activation predicate

if passcode.is-compliant == false:
  keep Wi-Fi inactive

when user updates passcode:
  reevaluate activation predicates
  activate Wi-Fi configuration
  send status report to server

关键点:

  • 这是概念流程,session 没有给出完整 Wi-Fi declaration JSON。
  • 服务端一次性下发密码策略和 Wi-Fi 配置。
  • 设备负责监听密码状态变化并重新计算谓词。
  • 服务端通过状态报告知道配置何时激活。

账户状态用数组增量上报

10:52)账户状态新增了八个 status items,覆盖 CalDAV、CardDAV、Exchange、Google、LDAP、incoming mail、outgoing mail 和 subscribed calendar。

{
  "status-items": [
    "account.list.caldav",
    "account.list.carddav",
    "account.list.exchange",
    "account.list.google",
    "account.list.ldap",
    "account.list.mail.incoming",
    "account.list.mail.outgoing",
    "account.list.subscribed-calendar"
  ]
}

关键点:

  • account.list.* 只报告由 configurations 安装的账户。
  • 手动创建的账户不会出现在这些状态里。
  • 通过 MDM profiles 安装的账户也不会出现在这里。
  • incoming mail 和 outgoing mail 分开报告。

11:19)每个账户状态项返回对应账户类型的 JSON 对象。identifier 是数组对象内的唯一标识,declaration-identifier 对应安装该账户的 configuration identifier。

{
  "identifier": "592D763E-C15B-44F8-A1FC-F88EB1901646",
  "declaration-identifier": "BF8FD199-467B-4BA5-886D-D82B7849E517",
  "hostname": "mail.example.com",
  "port": 443,
  "username": "user01",
  "is-mail-enabled": true,
  "are-notes-enabled": false
}

{
  "identifier": "592D763E-C15B-44F8-A1FC-F88EB1901646",
  "declaration-identifier": "BF8FD199-467B-4BA5-886D-D82B7849E517",
  "calendar-url": "https://holidays.example.com/country/US.ics",
  "username": "user01",
  "is-enabled": true
}

关键点:

  • identifier 用来在同一个状态数组里定位对象。
  • declaration-identifier 让服务端把状态对象关联回安装账户的 configuration。
  • hostnameportis-mail-enabledare-notes-enabled 是 mail 账户状态里的字段。
  • calendar-urlis-enabled 是 subscribed calendar 状态里的字段。
  • 服务端必须接受未来系统版本可能新增的未知 key。

12:11)数组值按对象增量上报。第一次订阅时,设备发送完整账户列表;新增账户时,只报告新增对象;账户状态改变时,只报告变化对象;移除账户时,报告带有 removed: true 的对象。

{
  "identifier": "592D763E-C15B-44F8-A1FC-F88EB1901646",
  "removed": true
}

关键点:

  • identifier 匹配服务端已有对象。
  • removed: true 表示服务端应删除本地保存的该对象状态。
  • 增量上报减少了服务端和设备之间传输的状态体积。
  • 设备会聚合最多约一分钟内的状态变化,以避免性能问题。

MDM App 状态消除安装进度轮询

17:13)MDM 服务端常常要知道 App 是否安装成功、是否被用户移除。过去可以用 InstalledApplicationListManagedApplicationList 命令轮询设备。新增的 mdm.app status item 让设备主动上报 MDM 安装的 App 状态。

{
  "management": {
    "client-capabilities": {
      "supported-payloads": {
        "status-items": [
          "mdm.app"
        ]
      }
    }
  }
}

关键点:

  • mdm.app 是一个数组状态项。
  • 每个数组对象代表一个由 MDM server 安装的 App。
  • 即使在 supervised devices 上,它也只报告由 MDM 安装的 App。
  • 数组对象按前面账户状态同样的规则增量上报。

17:35)App 状态报告里,identifier 是 bundle identifier,state 表示当前安装阶段。

{
  "StatusItems": {
    "mdm": {
      "app": [
        {
          "identifier": "com.apple.Pages",
          "name": "Pages",
          "version": "7358.0.134",
          "short-version": "12.0",
          "external-version-id": "844362702",
          "state": "managed"
        }
      ]
    }
  },
  "Errors": []
}

关键点:

  • StatusItems.mdm.app 是 MDM App 状态数组。
  • identifier 在这个状态里使用 App 的 bundle identifier。
  • name 是 App 名称。
  • versionshort-versionexternal-version-id 对应 ManagedApplicationList 响应里的版本信息。
  • state 表示安装阶段,例如 session 中提到的 promptinginstallingmanagedmanaged-but-uninstalled
  • Errors 为空数组表示该状态报告没有错误。

18:18)安装流程里,用户注册设备需要用户批准安装 App。设备可以先上报 prompting,用户点击安装后上报 installing,安装完成后上报 managed。如果用户手动删除 App,设备上报 managed-but-uninstalled。服务端随后可发送 RemoveApplication 移除内部维护的管理状态。

谓词可以查询数组状态项

20:40)activation predicate 使用 NSPredicate 字符串。WWDC22 增加的新语法把状态项名称放进 @status(...),旧语法继续兼容,但已经被标记为 deprecated。

22:15)当状态项是数组时,可以用 SUBQUERY 查找符合条件的元素。下面的例子检查设备上是否存在 bundle identifier 为 com.example.app 且状态为 managed 的 MDM App。

SUBQUERY(@status(mdm.app),
         $app,
         ($app.@key(identifier) == "com.example.app") AND ($app.@key(state) == "managed")
        ).@count == 1

关键点:

  • @status(mdm.app) 引用 mdm.app 状态数组。
  • $appSUBQUERY 遍历数组时使用的变量。
  • $app.@key(identifier) 读取数组对象里的 identifier key。
  • $app.@key(state) 读取数组对象里的 state key。
  • .@count == 1 表示恰好找到一个符合条件的对象。
  • 访问数组对象中的 key 时要使用 @key extension term。

Management properties 把服务器逻辑变成设备属性

24:10)服务端有时需要直接控制谓词计算。比如学校按教师角色分发配置:低年级或高年级 Wi-Fi、IT 管理员共享邮箱、体育教练日历。传统 MDM 往往要在设备分配给教师后逐个安装或移除 profiles。

WWDC22 新增 com.apple.management.properties declaration。服务端可以把自定义 JSON 属性写到设备上,再让 activation predicate 读取这些属性。

{
  "management": {
    "client-capabilities": {
      "supported-payloads": {
        "declarations": {
          "management": [
            "com.apple.management.properties"
          ]
        }
      }
    }
  }
}

关键点:

  • supported-payloads.declarations.management 列出设备支持的 management declaration 类型。
  • com.apple.management.properties 是这场 session 新增的 declaration 类型。
  • 它让服务端设置可被谓词读取的任意 JSON 属性。

24:40)management properties declaration 的 Payload 是服务端定义的 JSON 对象。值可以是字符串、数字、数组或对象。

{
  "Type": "com.apple.management.properties",
  "Identifier": "AAE09D73-6EF6-4F3B-9E15-11B0F86D5591",
  "ServerToken": "AB4C5B91-3E08-4D4E-A9FF-1E44FE5BFDD4",
  "Payload": {
    "name": "Student One",
    "age": 7,
    "roles": ["grade1", "spanish"]
  }
}

关键点:

  • Type 指定 declaration 类型。
  • Identifier 是该 declaration 的标识。
  • ServerToken 用于服务端标识自己发送的版本。
  • Payload.namePayload.agePayload.roles 都由服务端定义。
  • 多个 management properties declarations 可以同时存在,但 key 应该保持唯一;重复 key 会导致谓词取值不可预测。

24:53)activation predicate 用 @property(...) 读取这些属性。

{
  "Type": "com.apple.activation.simple",
  "Identifier": "076F928B-9D8E-4BA2-AD34-5655805C82D7",
  "ServerToken": "4FFA91BF-85AE-4053-B8FE-B1C3E507A9CB",
  "Payload": {
    "StandardConfigurations": [
      "3BBB4407-238A-44B1-ABB1-5E7AB95160E0"
    ]
  },
  "Predicate": "(@property(age) >= 18) AND (\"Grade12\" IN @property(roles))"
}

关键点:

  • Type 表示这是一个 simple activation。
  • Payload.StandardConfigurations 指向要被激活的配置。
  • Predicate 读取 management properties。
  • @property(age) >= 18 检查 age 属性。
  • "Grade12" IN @property(roles) 检查 roles 数组里是否包含指定角色。

27:24)学校场景的做法是先把完整 declarations 预置到设备上。设备分配给教师当天,服务端只发送一个包含教师角色的 management properties declaration。设备重新计算 activations,自动启用对应 Wi-Fi、邮箱或日历配置。

before assignment:
  preload Wi-Fi, mail, calendar declarations
  predicates are false on unassigned devices

on assignment day:
  send one management properties declaration with teacher roles
  reevaluate activations on device
  activate matching configurations

when role changes:
  update one management properties declaration
  reevaluate activations again

关键点:

  • 这是 session 描述的流程摘要,不是新的协议 API。
  • 预加载 declarations 减少了高峰期逐个下发 profiles 的压力。
  • 角色变化时,服务端只需要更新属性 declaration。
  • 复杂服务器逻辑被压缩成设备上的简单状态变化。

核心启发

1. 做一个密码合规驱动的网络配置开关

  • 做什么:把企业 Wi-Fi 或 VPN 配置绑定到密码合规状态。
  • 为什么值得做:session 明确说明 passcode.is-compliant 可以触发 activation predicate,避免服务端轮询。
  • 怎么开始:订阅 passcode.is-compliant,把敏感网络配置放进依赖该状态的 activation,观察配置激活后的 status report。

2. 做一个账户安装状态面板

  • 做什么:在 MDM 控制台展示由 configurations 安装的 mail、calendar、LDAP、Exchange 等账户状态。
  • 为什么值得做:账户状态对象带有 declaration-identifier,服务端可以把状态直接关联到下发的 configuration。
  • 怎么开始:订阅 account.list.mail.incomingaccount.list.mail.outgoingaccount.list.subscribed-calendar 等状态项,按 identifier 合并增量报告。

3. 做一个 App 安装进度流

  • 做什么:把 App 安装过程展示为 promptinginstallingmanagedmanaged-but-uninstalled 等状态变化。
  • 为什么值得做mdm.app 让设备主动上报 MDM 安装的 App 状态,减少 InstalledApplicationListManagedApplicationList 轮询。
  • 怎么开始:启用声明式管理后订阅 mdm.app,用 bundle identifier 合并数组对象,把 state 映射成控制台状态。

4. 做一个角色驱动的学校设备分配流程

  • 做什么:预加载配置,再用教师角色属性激活对应 Wi-Fi、邮箱和日历。
  • 为什么值得做com.apple.management.properties 可以把服务端角色逻辑变成设备上的 predicate 输入,适合开学日这类高峰场景。
  • 怎么开始:设计唯一的 roles 属性 key,用 @property(roles) 写 activation predicate,分配设备时只更新 management properties declaration。

5. 做一个状态数组增量同步器

  • 做什么:为账户状态和 MDM App 状态实现通用的数组对象合并逻辑。
  • 为什么值得做:session 说明数组值会按对象增量报告,新增、更新、删除都依赖 identifier
  • 怎么开始:以 identifier 建索引;收到未知 identifier 时插入;收到已知 identifier 时更新;收到 removed: true 时删除。

关联 Session

评论

GitHub Issues · utterances