首页 > 解决方案 > 连接应用程序 id 和密码时使用 powershell 处理 o365 邮箱中的电子邮件

问题描述

我正在编写一个 PowerShell 脚本,它可以读取 o365 邮箱中的电子邮件。

它必须连接到收件箱,阅读电子邮件并根据主题行做出决定,然后打开具有特定主题行的电子邮件并将电子邮件可能包含的任何附件下载到文件夹中。

[然后] 必须将处理后的邮件消息移动到邮箱子文件夹。

我过去曾使用 Exchange Web 服务和基本身份验证进行连接,但基本身份验证不再可用,因此我不得不使用现代身份验证技术重新编写脚本。

我从 Azure AD 管理员那里获得了一个应用程序 ID 和密码,并具有相关邮箱的权限。

我一直在谷歌搜索,并设法使用 Microsoft Graph 获得了一些方法 - 使用 Powershell 的这一部分:

$ClientID = "my-client-id"
$DirectoryID = "my-directory-id"
$ClientSecret = "my-client-secret"

$Credential = ConvertTo-GraphCredential -ClientID $ClientID -ClientSecret $ClientSecret -DirectoryID $DirectoryID

$mailbox = Get-EXOMailbox -UserPrincipalName myemailaccount@mycompany.com

这成功地让我获得了一个邮箱对象,但是从这里我不知道如何从中检索电子邮件进行处理。

Microsoft doco 和 Google 目前在如何从获取对象的邮箱中提取电子邮件方面对我没有太大帮助。

对相关教程有任何想法/建议或指示吗?

标签: powershelloffice365

解决方案


我设法弄清楚如何在不使用任何非标准库的情况下仅使用应用程序 ID 和机密来访问电子邮件。您需要安装 Graph 和 powershell 实用程序,但它们是 Microsoft 免费提供的,因此我称它们为标准。然后将它们导入到我的powershell脚本中:

Import-Module Microsoft.Graph.Mail
Import-Module Microsoft.PowerShell.Utility

之后,使用这些方法通过 microsoft graph REST API 访问您的 o365 电子邮件系统。

# This AuthenticateWithSecret is a slightly modified version of the method 
# described by https://adamtheautomator.com/powershell-graph-api/
function Get-AccessToken
{
    param(
        [string] $AppId,
        [string] $TenantName,
        [string] $AppSecret)

    $Scope = "https://graph.microsoft.com/.default"
    $Url = "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token"

    # Add System.Web for urlencode
    Add-Type -AssemblyName System.Web

    # Create body
    $Body = @{
        client_id = $AppId
        client_secret = $AppSecret
        scope = $Scope
        grant_type = 'client_credentials'
    }

    # Splat the parameters for Invoke-Restmethod for cleaner code
    $PostSplat = @{
        ContentType = 'application/x-www-form-urlencoded'
        Method = 'POST'
        # Create string by joining bodylist with '&'
        Body = $Body
        Uri = $Url
    }

    # Request the token!
    $Request = Invoke-RestMethod @PostSplat
    return $Request
}


# This method builds a  header object that can be passed
# in with each request to the Graph REST API, for authentication
# purposes
function Get-Header
{
    param($theRequest)

    $tokenType = $theRequest.token_type
    $accessToken = $theRequest.access_token

    # Create header
    $theHeader = @{
        Authorization = "$($tokenType) $($accessToken)"
    }
    return $theHeader
}


# This method gets an object containing the email folders from
# a mailbox specified by its UserPrincipalName - which is typically
# the email address of the user concerned.  By default it will return
# the first 200 folders it finds, but you can specify however many
# that you wish to return using the $numberOfFoldersToGet parameter.
function Get-Folders
{
    param(
        $Credential,
        [string] $UserPrincipalName,
        [int]$numberOfFoldersToGet=200)

    $Header = Get-Header -theRequest $Credential
    $restUrl = "https://graph.microsoft.com/v1.0/users/$UserPrincipalName/mailFolders/?`$top=$numberOfFoldersToGet"
    $folderResult = Invoke-RestMethod -Uri $restUrl -Headers $Header -Method Get -ContentType "application/json" 
    return $folderResult
}


# This is a little helper function to get the specific folder object
# from an array of folder objects.  You specify the folder that you 
# want in the array based o its displayName using the $folderNameToFind
# parameter.
function Get-FolderFromListOfFolders
{
    param($listOfFolders,
          $folderNameToFind)

    $specificFolder = ""

    # Yeah, yeah, I know - we're doing this the brute-force way - just 
    # looping through all the folders until we find the one we want.  
    # Unless you have an insane number of folders, this shouldn't take 
    # *that* long, but this loop could be re-written to use a nicer search
    # if it's really that big a problem.

    foreach($fdr in $allFolders)
    {
        $thisFolderName = $fdr.displayName
        if($thisFolderName -eq $folderNameToFind)
        {
            $specificFolder = $fdr
            break
        }
    }
    return $specificFolder
}


# This function allows you to retrieve an object describing a specific
# mail folder in an o365 Outlook, which you can specify by name.  It allows
# you to access any folder by name - not just the common ones.
function Get-SpecificFolder
{
    param(
        $Credential,
        [string] $UserPrincipalName,
        [string] $folderName)

    $allTheFolders = Get-Folders -Credential $Credential -UserPrincipalName $UserPrincipalName
    $allFolders = $allTheFolders.value
    $specificFolder = Get-FolderFromListOfFolders -listOfFolders $allFolders -folderNameToFind $folderName
    $folderId = $specificFolder.id

    $Header = Get-Header -theRequest $Credential
    $theRestQuery = "https://graph.microsoft.com/v1.0/users/$UserPrincipalName/mailFolders/$folderId"
    $folderResult = Invoke-RestMethod -Uri $theRestQuery -Headers $Header -Method Get -ContentType "application/json" 
    return $folderResult
}


# This function returns an object containing all the emails in a given 
# Mail folder in Outlook in o365
function GetEmails
{
    param(
        $Credential,
        [string] $UserPrincipalName,
        [string] $folderId)

    $Header = Get-Header -theRequest $Credential
    $restUrl = "https://graph.microsoft.com/v1.0/users/$UserPrincipalName/mailFolders/$folderId/messages"
    $emailResult = Invoke-RestMethod -Uri $restUrl -Headers $Header -Method Get -ContentType "application/json"
    return $emailResult
}

您可以通过这种方式使用这些方法。首先,您需要指定这些变量

$ClientID = "My-azure-ad-client-id"
$DirectoryID = "My-directory-id"
$ClientSecret = "My-client-secret"
$MailboxName = "the-email-address-of-the-o365-mailbox-i-want-to-access"
$MailboxFolderName = "the-folder-name-of-the-mailfolder-containing-emails"

然后,您可以获得一个凭证对象:

$Credential = Get-AccessToken -AppId  $ClientID -TenantName $DirectoryID -AppSecret $ClientSecret

然后获取您的电子邮件文件夹对象

$myfolder = Get-SpecificFolder -Credential $Credential -UserPrincipalName $MailboxName -folderName $MailboxFolderName

然后,获取您的文件夹的 ID - 这允许您访问任何文件夹 - 甚至是非标准文件夹的名称。

$folderId = $myfolder.id

现在,从文件夹中获取电子邮件对象

$emails = GetEmails -Credential $Credential -UserPrincipalName $MailboxName -folderId $folderId

然后获取实际的电子邮件数组

$theEmails = $emails.value

现在遍历您的电子邮件数组并用它做一些事情。

foreach($email in $theEmails)
{
    Write-Output $email.subject
}

推荐阅读