Azure Local离线模式注册(系列篇之十)

1. 注册流程性质

✅ 官方要求

官方原文:"On a machine with internet access, ensure that Azure CLI and Azure PowerShell are installed."

Register 阶段必须联网机器上一次性执行------是云端元数据登记,本地环境依然完全离线。

注册采用 self-attestation(自我声明) 流程:声明硬件配置 → 推送到 Azure → 部署资源上的 banner 消失。

🔍 技术分析

Register 阶段并不会改变本地 Azure Local 或 Appliance 的配置

它的作用是:向 Azure Resource Manager(Microsoft.Edge Resource Provider)提交 Hardware Settings 元数据

因此:

  • Appliance 仍保持 Air-gapped(完全离线)
  • Control Plane 不建立 长期 Azure 连接
  • Register 仅完成 Azure Resource Metadata 更新

这就是官方一句话背后的真正含义:Register 是"声明",不是"握手"。

💡 企业最佳实践

  • Register 建议在 Appliance 部署完成并验证正常后立即执行------便于后续 Support 链路打通
  • 保存本次 Register 的 PowerShell 输出和提交参数 (含 $body JSON、响应 payload)------便于后续扩容、审计、重新登记时比对
  • 如果管理集群发生硬件变更 (节点扩容、CPU/内存调整等),应关注是否需要重新提交 Hardware Settings,并以最新官方要求为准

2. Self-Attestation 函数(官方原文照搬)

复制代码
function New-AzureLocalDisconnectedOperationsSelfAttestation {
    <#
    .SYNOPSIS
    Registers your disconnected operations resource using a self-attestation flow.
    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateSet('AzureCloud', 'AzureUSGovernment')]
        [string]$Cloud,

        [Parameter(Mandatory)]
        [string]$SubscriptionId,

        [Parameter(Mandatory)]
        [string]$ResourceGroup,

        [Parameter(Mandatory)]
        [string]$ResourceName,

        [Parameter(Mandatory)]
        [int]$TotalCores,

        [Parameter(Mandatory)]
        [int]$DiskSpaceInGb,

        [Parameter(Mandatory)]
        [int]$MemoryInGb,

        [Parameter(Mandatory)]
        [string]$Oem,

        [Parameter(Mandatory)]
        [string]$HardwareSku,

        [Parameter(Mandatory)]
        [int]$Nodes,

        [Parameter(Mandatory)]
        [string]$VersionAtRegistration,

        [Parameter(Mandatory)]
        [string]$SolutionBuilderExtension,

        [Parameter(Mandatory)]
        [string]$DeviceId,

        [string]$ApiVersion = '2026-03-01-preview',

        [switch]$SkipLogin
    )

    if (-not $SkipLogin) {
        Connect-AzAccount -EnvironmentName $Cloud -ErrorAction Stop | Out-Null
    }

    Select-AzSubscription -SubscriptionId $SubscriptionId -ErrorAction Stop | Out-Null

    function Get-BearerToken {
        param(
            [Parameter(Mandatory)]
            [ValidateSet('AzureCloud', 'AzureUSGovernment')]
            [string]$CloudEnvironment
        )

        $resource = switch ($CloudEnvironment) {
            'AzureCloud'        { 'https://management.azure.com/' }
            'AzureUSGovernment' { 'https://management.usgovcloudapi.net/' }
        }

        $tokenResponse = Get-AzAccessToken -ResourceUrl $resource -ErrorAction Stop
        $token = $tokenResponse.Token
        if ($token -is [System.Security.SecureString]) {
            $plainTextToken = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToCoTaskMemUnicode($token))
        } else {
            $plainTextToken = $token
        }
        return "Bearer $plainTextToken"
    }

    $baseEndpoint = switch ($Cloud) {
        'AzureCloud'         { 'management.azure.com' }
        'AzureUSGovernment'  { 'management.usgovcloudapi.net' }
    }

    $token = Get-BearerToken -CloudEnvironment $Cloud
    Write-host "Obtained authorization token $token." -ForegroundColor Green
    $uri = "https://$baseEndpoint/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroup/providers/Microsoft.Edge/disconnectedOperations/$ResourceName/hardwareSettings/default?api-version=$ApiVersion"

    $body = @{
        properties = @{
            totalCores               = $TotalCores
            diskSpaceInGb            = $DiskSpaceInGb
            memoryInGb               = $MemoryInGb
            oem                      = $Oem
            hardwareSku              = $HardwareSku
            nodes                    = $Nodes
            versionAtRegistration    = $VersionAtRegistration
            solutionBuilderExtension = $SolutionBuilderExtension
            deviceId                 = $DeviceId
        }
    } | ConvertTo-Json -Depth 5

    Write-Host "Creating HardwareSetting for '$ResourceName'..." -ForegroundColor Cyan
    Write-Host "  PUT $uri" -ForegroundColor DarkGray

    $response = Invoke-RestMethod -Method Put -Uri $uri -Headers @{
        'Authorization' = $token
        'Content-Type' = 'application/json'
    } -Body $body

    Write-Host "HardwareSetting created successfully." -ForegroundColor Green
    $response | ConvertTo-Json -Depth 10
}

🔍 技术分析:Self-Attestation 实际执行流程

整个函数实际上就是 5 步标准 ARM REST API 调用 ------不是什么神秘

复制代码
       Internet PC
              │
              ▼
      Connect-AzAccount
              │
              ▼
      Get-AzAccessToken
              │
              ▼
      Invoke-RestMethod (PUT)
              │
              ▼
      Azure Resource Manager
              │
              ▼
      Microsoft.Edge RP
              │
              ▼
      hardwareSettings/default

即:获取 OAuth Token → PUT 一段 JSON → 写 Microsoft.Edge/disconnectedOperations/.../hardwareSettings/default 资源。

本质就是 ARM REST API------没有远程探测、没有持续心跳、没有 TPM 验证。

🔍 技术分析:Self-Attestation 不是 TPM Attestation,也不是 Arc Registration

Self-Attestation 不是 TPM Attestation,也不是 Azure Stack HCI 的 Arc Registration。

它只是用户主动声明(Self Declare)当前管理集群的硬件配置,并提交给 Azure Resource Provider,作为 Disconnected Operations Resource 的资产信息。

因此,微软当前并不会在 Register 阶段主动远程验证本地硬件,而是依据用户提交的数据完成资源登记。

这是根据 ARM Resource Provider 的调用方式得出的技术分析:整个流程只执行了一次 PUThardwareSettings没有后续对本地环境的远程探测或持续连接

这种分析既符合脚本实现,也能帮助读者理解 Self-Attestation 的真正含义,而不是把它误认为远程度量证明或硬件可信验证。


3. 调用参数示例(官方原文)

复制代码
# Modify these to fit your disconnected operations resource.
# DeviceId 可以通过 Get-ApplianceInstanceConfiguration 在已部署环境获取

$params = @{
    Cloud                      = 'AzureCloud'
    SubscriptionId             = ''
    ResourceGroup              = ''
    ResourceName               = ''
    TotalCores                 = 320   # 总物理核
    DiskSpaceInGb              = 2000  # 管理集群总磁盘
    MemoryInGb                 = 2000  # 总内存
    Oem                        = ''    # Dell / Lenovo / HPE
    HardwareSku                = ''    # AX-760 / MX 655
    Nodes                      = 10    # 管理节点数
    VersionAtRegistration      = '2602.2.32510'
    SolutionBuilderExtension   = 'x.y.z'
    DeviceId                   = ''    # 从 portal 拷贝
    ApiVersion                 = '2026-03-01-preview'
}

New-AzureLocalDisconnectedOperationsSelfAttestation @params

🔍 技术分析:参数语义精解

|----------------------------|----------------------|--------------------------------------------------------------------|---------------------------------------------|
| 参数 | 官方示例值 | 实际含义 | 常见误填 |
| TotalCores | 320 | 整个管理集群所有节点的物理 CPU Core 总数 ------不是 Logical Processor(含 HT) | 误填 logical processor:640 |
| DiskSpaceInGb | 2000 | 管理集群可用于 Azure Local 的总物理容量(GB) ------不是"管理集群总磁盘" | 误填"总磁盘"------Disk(物理盘)和 Capacity(可用容量)不是一回事 |
| MemoryInGb | 2000 | 所有节点物理内存总和 ------不是 "可用内存" | 误填 free memory |
| Oem | '' | Dell / Lenovo / HPE | --- |
| HardwareSku | '' | OEM catalog 中的具体 SKU,例 AX-760 / MX 655 | --- |
| Nodes | 10 | 管理节点数 | --- |
| VersionAtRegistration | 2602.2.32510 | 应与 Appliance / Operations Module / Azure Local Build 保持一致 | 误填 2601(实际 Appliance 2602)→ 部署验证可能失败 |
| SolutionBuilderExtension | x.y.z | OEM Solution Builder 提供的扩展版本号 | --- |
| DeviceId | '' | 来自已部署的 Appliance(不是用户手工填写) | 误以为需要自己生成 |
| ApiVersion | 2026-03-01-preview | ARM API 版本,以官方 Register 文档为准 | --- |

💡 企业最佳实践

  • TotalCores 不要 包含 Hyper-Threading 后的逻辑核------按物理核统计(例:3 节点 × 32 物理核 × HT 关闭 = 96,不是 192)
  • DiskSpaceInGb 扣除 RAID/热备/系统保留后的净可用容量------这才是"可用于 Azure Local 的总物理容量"
  • VersionAtRegistration 必须在 Register 之前从已部署的 Appliance 读取(详见 deploy-appliance.md § 版本一致性)
  • 保存完整 JSON body 和 response payload------便于后续审计/扩容

4. 成功标志

✅ 官方要求

  • 控制台输出 'Hardware setting created successfully.'
  • Azure Portal 上的 banner 消失
  • Portal 上资源的硬件属性填充为登记信息

🔍 技术分析:真正的成功标准

Portal Banner 只是 UI 表现 ------真正成功标准是 ARM 资源可读

复制代码
# 验证 1:GET hardwareSettings 应返回 200 OK + 实际数据
$uri = "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroup/providers/Microsoft.Edge/disconnectedOperations/$ResourceName/hardwareSettings/default?api-version=$ApiVersion"
Invoke-RestMethod -Method Get -Uri $uri -Headers @{ 'Authorization' = $token }
  • 200 OK + JSON body → 资源登记成功
  • 404 Not Found → Register 未生效或资源名错误
  • 409 Conflict → 资源已存在但状态冲突

Portal 只是 UI 表现;ARM 才是真理。

💡 企业最佳实践

  • Register 完成后立即跑一次 GET 验证------确认 ARM 端状态一致
  • 保留 GET 响应 payload ------与提交时的 $body JSON 一起存档
  • 如果 Portal UI 出现延迟(极端场景),以 ARM GET 结果为准

5. 为什么必须联网

官方只说:"On a machine with internet access..."

🔍 技术分析:联网边界分析

真正需要联网的是:

  • Connect-AzAccount(OAuth 登录)
  • Get-AzAccessToken(拿 Bearer Token)
  • Invoke-RestMethod PUT 到 management.azure.com

整个 Appliance 本身完全不用联网 ------无论是 Install-Appliance、日常运维、Control Plane 通信,都不依赖 Azure

因此:Air-gapped 模式并没有被破坏 ------只是 Register 阶段临时借用一台 Internet PC 完成元数据登记。

很多客户最关心这一点------Register 是"声明动作",不是"持续握手"。

💡 企业最佳实践

  • 选择临时出站的 Internet PC ------可以是 DMZ 的跳板机,不推荐用管理集群节点直接出站
  • Register 完成后立即恢复网络隔离------Internet PC 拔网线或重置防火墙
  • 保存 Register 时间戳------便于后续审计("何时向 Azure 声明了这些硬件")

6. Register 背后的技术原理

官方没有提供 Register 阶段的架构图------但理解数据流有助于:

  • 排查 Register 失败
  • 理解 Self-Attestation 的局限
  • 评估 Air-gapped 合规性

关键解读

  1. 数据流是单向的 ------只有 Internet PC → Azure 的出站,Appliance 完全不参与
  1. DeviceId 是唯一关联------Internet PC 用 Appliance 提供的 DeviceId 完成 PUT
  1. Appliance 始终离线------Air-gapped 边界未被破坏
  1. Portal Banner 只是副作用 ------真正的成功是 ARM 端的 hardwareSettings 资源可读

7. 技术分析(官方未展开说明)

本节专门解释官方文档没有明说的技术原理,按 ARM Resource Provider 行为推断。

7.1 Register 是一次性还是周期性

官方没说------但从 ARM Resource Provider 实现来看:

Register 本质是创建(或更新) Microsoft.Edge/disconnectedOperations/.../hardwareSettings 资源

从 ARM RP 实现来看,它属于资源元数据 ,而不是周期性心跳 ,因此通常只需首次完成 Register

如果硬件规模发生变化(如节点扩容),则可能需要重新更新 Hardware Settings------以最新官方要求为准。

7.2 Hardware Information 是否会校验

官方没说。但从技术逻辑推断:

Hardware Settings 主要用于 Azure 对 Disconnected Operations Resource 的资产登记

因此:

  • TotalCores / MemoryInGb / DiskSpaceInGb 应尽量与实际管理集群一致
  • 即使官方没有说明校验策略,也不建议故意填写与实际环境不一致的数据
  • 这与 §4 真正成功标准 配合:以 ARM GET 响应为准

7.3 SolutionBuilderExtension 是什么

官方没说

技术分析

SolutionBuilderExtension 更像是 OEM Solution Builder 提供的扩展版本号

  • 使用 Microsoft 官方参考实现时:保持官方提供的默认值即可
  • OEM(Dell/HPE/Lenovo)未来可能提供自己的 Extension Version------以 OEM 文档为准
  • 部署时如果不指定,使用 x.y.z 占位符通常可以接受,但生产环境建议填实际值

7.4 DeviceId 的来源

官方说明Get-ApplianceInstanceConfiguration 在 OperationsModule 里。

技术分析

DeviceIdAppliance Deployment 生成的唯一标识 ------Register 阶段只是引用 该标识,不会重新生成

因此:

  • DeviceId 应来自已部署完成的 Appliance,而不是用户手工填写
  • 获取命令必须在 Appliance 部署完成后的 OperationsModule 会话中执行
  • 不要 自己编一个 DeviceId------会导致 ARM 资源与本地 Appliance 无法关联

7.5 ApiVersion = 2026-03-01-preview

官方明确提供该值,但仍要理解:

  • Register 调用本质属于 ARM REST API
  • ApiVersionMicrosoft.Edge Resource Provider 生命周期保持一致
  • Preview API 在未来 GA 后可能发生变更 ------建议始终以官方 Register 文档提供版本为准

7.6 Register 失败的常见原因

|----------------------|-----------------------------------|--------------------------------------------------------|
| 症状 | 可能原因 | 排查方向 |
| 401 Unauthorized | Token 过期 / Subscription ID 错误 | Get-AzContext 检查登录状态 |
| 404 Not Found | ResourceName / ResourceGroup 拼写错误 | Portal 端核对资源名 |
| 409 Conflict | 已 Register 过但值不一致 | GET hardwareSettings 看现有值,比对 |
| 403 Forbidden | RBAC 不足 | 需 Microsoft.Edge/disconnectedOperations/.../write 权限 |
| Invoke-RestMethod 超时 | Internet PC 出站被防火墙拦 | 检查 management.azure.com 443 可达性 |

官方未提供失败排查指南------以上为基于 ARM RP 行为的常见推断。生产环境请以 Microsoft Support 实际反馈为准。

相关推荐
XUHUOJUN1 天前
Azure Local离线模式PKI规划(系列篇之五)
microsoft·azure local
CHS_Lab3 个月前
DELL服务器阵列崩溃恢复方法
服务器·数据恢复·dell·raid·阵列恢复·戴尔恢复·服务器恢复
特别关注外国供应商3 个月前
Cohesity 和 Dell 推出一项全新的、经过验证的解决方案!
备份·集成·数据管理·dell·归档·cohesity·netbackup
zz9602261 年前
DELL IDRAC配置
dell
驱动小百科2 年前
dell电脑开不了机怎么回事?戴尔电脑无法开机解决方法
电脑·dell·戴尔电脑·驱动更新·戴尔电脑无法开机·电脑无法开机
sysin.org2 年前
VMware ESXi 8.0U3b macOS Unlocker & OEM BIOS 2.7 Dell HPE 定制版 9 月更新发布
macos·esxi·dell·浪潮·oem·hpe·ieit
178550693 年前
戴尔游匣笔记本Dell G15 5510原装出厂Windows11.21H2系统
恢复出厂系统·原厂系统·dell·戴尔g15·戴尔5510系统