PAYATHON 2026

Get-UsageAggregates 中 continuationtoken 参数无效

数据小陈

结论

第一次调用 Get-UsageAggregates 时,不要传入空的 -ContinuationToken。只有上一页响应返回了非空令牌,才能添加该参数。令牌必须原样传递,不能用完整的 NextLink 代替,也不要自行拼接、截断或重复编码。

出现这类错误,通常不是因为订阅没有用量数据,而是脚本向接口发送了无效参数,例如:

continuationtoken=

原因

-ContinuationToken 用于分页。第一次请求没有上一页,自然不需要这个参数。

一些旧脚本会先把变量初始化为空,然后在调用命令时无条件传入:

$continuationToken = $null

Get-UsageAggregates `
    -ReportedStartTime $reportedStartTime `
    -ReportedEndTime $reportedEndTime `
    -ContinuationToken $continuationToken

即使 PowerShell 变量是 $null 或空字符串,旧版 cmdlet 仍可能把这个参数提交给服务端。服务端发现参数存在,却没有有效值,就会返回:

InvalidInput: Parameter continuationtoken was missing or had an unacceptable value.

后续分页请求也可能遇到相同错误。常见原因包括传入了完整的 NextLink,或者对令牌又做了一次 URL 编码。

处理方法

第一次请求应完全省略 -ContinuationToken:

$usageData = Get-UsageAggregates `
    -ReportedStartTime $reportedStartTime `
    -ReportedEndTime $reportedEndTime `
    -AggregationGranularity Daily `
    -ShowDetails

如果响应对象提供了 ContinuationToken,再继续获取后续页面:

$allUsage = @()

$usageData = Get-UsageAggregates `
    -ReportedStartTime $reportedStartTime `
    -ReportedEndTime $reportedEndTime `
    -AggregationGranularity Daily `
    -ShowDetails

$allUsage += $usageData.UsageAggregations

while (-not [string]::IsNullOrWhiteSpace($usageData.ContinuationToken)) {
    $usageData = Get-UsageAggregates `
        -ReportedStartTime $reportedStartTime `
        -ReportedEndTime $reportedEndTime `
        -AggregationGranularity Daily `
        -ShowDetails `
        -ContinuationToken $usageData.ContinuationToken

    $allUsage += $usageData.UsageAggregations
}

ContinuationToken 为空时应结束循环,不要继续发送空参数。

使用参数集合避免空值

如果现有脚本不方便分别处理第一次请求和后续请求,可以使用 splatting 动态添加参数:

$continuationToken = $null
$allUsage = @()

do {
    $parameters = @{
        ReportedStartTime      = $reportedStartTime
        ReportedEndTime        = $reportedEndTime
        AggregationGranularity = 'Daily'
        ShowDetails            = $true
    }

    if (-not [string]::IsNullOrWhiteSpace($continuationToken)) {
        $parameters['ContinuationToken'] = $continuationToken
    }

    $usageData = Get-UsageAggregates @parameters
    $allUsage += $usageData.UsageAggregations

    $continuationToken = $usageData.ContinuationToken
}
while (-not [string]::IsNullOrWhiteSpace($continuationToken))

这样可以确保第一次请求中没有 ContinuationToken 参数。

不同版本的 Azure PowerShell 模块可能采用不同的返回结构。如果对象没有 ContinuationToken,只有 NextLink,需要从查询字符串中提取令牌,不能把整个链接直接传给 -ContinuationToken:

$continuationToken = $null

if ($usageData.NextLink -match '[?&]continuationtoken=([^&]+)') {
    $continuationToken = [System.Uri]::UnescapeDataString($matches[1])
}

提取令牌后,仍要先检查它是否为空:

if (-not [string]::IsNullOrWhiteSpace($continuationToken)) {
    $usageData = Get-UsageAggregates `
        -ReportedStartTime $reportedStartTime `
        -ReportedEndTime $reportedEndTime `
        -AggregationGranularity Daily `
        -ShowDetails `
        -ContinuationToken $continuationToken
}

如果响应对象已经包含 ContinuationToken 属性,应优先使用该属性,避免自行解析 NextLink。

仍然报错时的检查项

可以先查看实际传入的值:

$continuationToken | Format-Hex

也可以检查长度:

$continuationToken.Length

需要逐项确认:

  • 令牌是否为 $null、空字符串或只包含空白字符。
  • 是否误将完整的 NextLink 传给了 -ContinuationToken。
  • 是否调用过 Trim() 或字符串拆分等操作,导致令牌被截断。
  • 是否对已经编码的令牌再次进行了 URL 编码。
  • 是否保存令牌后隔了较长时间才继续请求。分页令牌通常只用于紧接着获取下一页,不应作为长期凭据保存。
  • ReportedStartTime 和 ReportedEndTime 是否有效,以及开始时间是否早于结束时间。

关于旧版命令

Get-UsageAggregates 是较早期的 Azure 用量查询方式。旧博客中提到的模块名称、返回对象和分页行为,可能与当前环境不同。如果删除空令牌后仍然失败,可以先确认实际使用的模块和命令来源:

Get-Command Get-UsageAggregates | Format-List Name, Source, Version

如果仍需维护旧脚本,直接按上述方式修正分页逻辑即可。新建成本或用量采集程序时,应优先评估当前的 Azure Cost Management API、导出功能或对应的新版 PowerShell 命令。旧博客中的接口和模块不适合作为新系统的长期依赖。

备注:内容仅供参考。