You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PowerShell中如何设置GraphAPI令牌以标记Office365邮件为已读?

问题描述

我用PowerShell从Office 365获取邮件,当前令牌获取代码能正常读取邮件,但调用自定义函数MarkSingleEmailAsRead标记特定邮件为已读时,返回错误:

Error Message: The remote server returned an error: (401) Unauthorized.

令牌获取代码

if ($token -ne $null) 
{
    $body = @{
        client_id     = $clientID
        scope         = "https://graph.microsoft.com/.default"
        client_secret = $clientSecret
        grant_type    = "client_credentials"
    }

    $URL = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
    try { $tokenRequest = Invoke-WebRequest -Method Post -Uri $URL  -ContentType "application/x-www-form-urlencoded" -Body $body -UseBasicParsing -ErrorAction Stop }
    catch { 
        Write-Host "Unable to obtain access token, aborting..." 
        Write-Host "Error Message: $($Error[0])"
        return 
        }

    $token = ($tokenRequest.Content | ConvertFrom-Json).access_token
    #Write-Host("Got Token=$token") 
} 

$authHeader1 = @{
   'Content-Type'='application\json'
   'Authorization'="Bearer $token"
}

标记邮件为已读的函数代码

function MarkSingleEmailAsRead ($emailId, $headers)
{
    <# This is what we need to post as an example 
    POST https://graph.microsoft.com/v1.0/admin/serviceAnnouncement/messages/markRead
    Content-Type: application/json

    {
      "messageIds": ["MC172851", "MC167983"]
    }
    #>

    $graphApiPostUrl = "https://graph.microsoft.com/v1.0/admin/serviceAnnouncement/messages/markRead"
    $body = @{
       "messageIds" = @($emailId) 
    }    

    Write-Host "Headers=" 
    $headers | ConvertTo-Json | Write-Host 
    Write-Host "Body=" 
    $body | ConvertTo-Json | Write-Host 


    try { 
        $results = Invoke-WebRequest -Method Post -Uri $graphApiPostUrl -ContentType "application/x-www-form-urlencoded" 
                      -Body $body -Headers $headers -UseBasicParsing -ErrorAction Stop 
        }
    catch { 
        Write-Host "Error Message: $($Error[0])"
        return 
        }

}

我想用同一令牌同时实现邮件读取和更新(标记为已读),想知道如何修改scope参数(当前为scope = "https://graph.microsoft.com/.default"),之前尝试两种配置都无效,同时不确定是否需要在Azure端配置更新权限。


解决方案

1. Azure AD应用权限配置(核心)

你用的是客户端凭证模式(client_credentials),这种模式下令牌的权限完全依赖于Azure AD应用注册中配置的应用权限(而非委派权限):

  • 登录Azure门户,找到你的应用注册,进入「API权限」页面
  • 点击「添加权限」→ 选择「Microsoft Graph」→ 「应用权限」
  • 添加以下权限(必须由租户管理员授予同意):
    • ServiceAnnouncementMessage.ReadWrite.All:对应标记服务公告邮件为已读的权限
    • 保留原有的读取权限(比如ServiceAnnouncementMessage.Read.All)
  • 添加完成后,点击「授予管理员同意」,确保权限生效

2. Scope参数无需修改

客户端凭证模式下,scope = "https://graph.microsoft.com/.default"是正确写法,它会自动包含所有已配置并授予同意的应用权限,不需要手动指定单个scope。

3. 修复函数中的请求错误

你的MarkSingleEmailAsRead函数存在两个关键错误,会导致请求失败:

  • Content-Type错误:Graph API要求该接口的Content-Type为application/json,而非application/x-www-form-urlencoded
  • Body未转成JSON字符串:直接传入PowerShell哈希表作为Body,需要先转换为JSON格式

修改后的函数代码:

function MarkSingleEmailAsRead ($emailId, $headers)
{
    $graphApiPostUrl = "https://graph.microsoft.com/v1.0/admin/serviceAnnouncement/messages/markRead"
    $body = @{
       "messageIds" = @($emailId) 
    } | ConvertTo-Json

    Write-Host "Headers=" 
    $headers | ConvertTo-Json | Write-Host 
    Write-Host "Body=" 
    $body | Write-Host 

    try { 
        $results = Invoke-WebRequest -Method Post -Uri $graphApiPostUrl -ContentType "application/json" 
                      -Body $body -Headers $headers -UseBasicParsing -ErrorAction Stop 
        }
    catch { 
        Write-Host "Error Message: $($Error[0])"
        return 
        }
}

4. 验证步骤

  1. 重新获取令牌(确保权限更新已同步)
  2. 调用修改后的函数,检查是否能成功标记邮件为已读

内容的提问来源于stack exchange,提问作者NealWalters

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 17:55:31