![如何在 Exchange 混合环境中将 Office 365 电子邮件备份/导出到 pst?](https://linux22.com/image/721644/%E5%A6%82%E4%BD%95%E5%9C%A8%20Exchange%20%E6%B7%B7%E5%90%88%E7%8E%AF%E5%A2%83%E4%B8%AD%E5%B0%86%20Office%20365%20%E7%94%B5%E5%AD%90%E9%82%AE%E4%BB%B6%E5%A4%87%E4%BB%BD%2F%E5%AF%BC%E5%87%BA%E5%88%B0%20pst%EF%BC%9F.png)
我们有一个交换混合环境. (有些用户在本地 Exchange 中,有些用户在 Office 365 中。)
当我们想要从本地我们使用下面的命令来导出邮箱和存档。
New-MailboxExportRequest -Mailbox "user" -FilePath \\mysrv\l$\PST\Mailbox-user.pst; New-MailboxExportRequest -Mailbox "user" -IsArchive -FilePath \\mysrv\l$\PST\Mailbox-user-archive.pst -confirm:$false
New-MailboxExportRequest 对于本地用户来说运行良好,但对于 Office 365 来说却不行。有没有办法使用 powershell 将 Office 365 用户邮箱导出到 pst?
到目前为止我已经尝试过:
我登录了 Office 365
$UserCredential = Get-Credential
Import-Module MSOnline
Connect-MsolService -Credential $UserCredential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $UserCredential -Authentication Basic -AllowRedirection
Import-PSSession $Session
并尝试过New-MailboxExportRequest
但它会产生一个错误。显然 Office 365 不知道该命令
PS C:\Users\pp> New-MailboxExportRequest
New-MailboxExportRequest : The term 'New-MailboxExportRequest' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:1
+ New-MailboxExportRequest
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (New-MailboxExportRequest:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Get-Command *Export*
结果如下
尝试谷歌搜索,但找不到可行的选项。有人可以指导我吗?办公环境中执行此操作的兼容命令是什么?
附言:
我已经尝试https://www.codetwo.com/admins-blog/how-to-export-office-365-mailboxes-to-pst-using-ediscovery/
使用 E5 许可证,它在 GUI 中完美运行。但我担心的是,即使是电子取证,有了许可证,是否可以使用 powershell 来做到这一点?我的意思是通过 powershell 编写脚本/自动化?
答案1
您无法使用内置工具通过 PowerShell 将 Exchange Online 邮箱直接导出到 PST。所需的 New-MailboxExportRequest 不存在于 Online(或未向我们普通人公开)。
你可以:
- eDiscovery,似乎仅有 GUI。
- 将邮箱卸载/迁移到本地 Exchange 并在本地运行 New-MailboxExportRequest(如果需要,迁移回 Exchange Online)
- 使用各种第三方工具,通过 EWS 或 MAPI 执行导出
- 编写脚本委派完全访问权限,将 Outlook 绑定到委派邮箱并导出到 PST。从技术上讲,这很有可能,但我从未见过有人这样做。我还没有深入研究过 eDiscovery,但我相信 eDiscovery 导出到 PST 的方式大致如此(旧 Exchange 也用于绑定到 Outlook 以导出 PST)。但如果没有丰富的 MAPI 经验,Outlook COM 模型使用起来会相当复杂(我编写过一些 Outlook 脚本,但模拟 PST 导出至少可以说是具有挑战性的)。
我和你一样沮丧。为即将离职的用户导出邮箱进行长期存储是一件很烦人的事情。如果我们可以像导入一样导出到 Azure Blob,那将是一个好的开始。
答案2
这不是问题的答案:我还没有尝试直接从 Exchange Online 导出到 PST。
这是对 @DonZoomiks 回答的最后一点的补充。Powershell 的一部分,来自各种晦涩难懂的来源,并从 Exchange Online 中提取邮件。根据需要进行自定义。
该产品是包含已检索邮件的自定义对象。也许有人可以研究并分享如何从那里到 PST 而不通过 Outlook?
要求是Exchange Web 服务 (EWS) 托管 API 2.2
前两个功能:
function Enter-ExchangeOnlineSession {
<#
.Synopsis
Sets up a user session with ExchangeOnline, typically to fetch mail.
.DESCRIPTION
Sets up a user session with ExchangeOnline, typically to fetch mail.
.EXAMPLE
$CredMail = Get-Credential -UserName [email protected]
$Service = Enter-ExchangeOnlineSession -Credential $CredMail -MailDomain domain.com
.INPUTS
Inputs to this cmdlet (if any)
.OUTPUTS
Output from this cmdlet (if any)
.NOTES
General notes
.COMPONENT
The component this cmdlet belongs to
.ROLE
The role this cmdlet belongs to
.FUNCTIONALITY
The functionality that best describes this cmdlet
#>
Param(
[Parameter(ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[ValidateNotNullOrEmpty()]
[string]$EWSAssemblyPath = 'C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll',
# Parameter Credential
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
ValueFromRemainingArguments=$false,
Position=1)]
[System.Management.Automation.CredentialAttribute()]
$Credential,
# Parameter Maildomain
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
ValueFromRemainingArguments=$false,
Position=2)]
[string]$MailDomain,
# Parameter Maildomain
[Parameter(Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
ValueFromRemainingArguments=$false,
Position=3)]
[string]$AutoDiscoverCallbackUrl = 'https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml'
)
# Set the path to your copy of EWS Managed API and load the assembly.
[void][Reflection.Assembly]::LoadFile($EWSAssemblyPath)
# Establish the session and return the service connection object.
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService
$Service.Credentials = New-Object System.Net.NetworkCredential($Credential.UserName, $Credential.GetNetworkCredential().Password, $MailDomain)
$TestUrlCallback = {
param ([string] $url)
if ($url -eq $AutoDiscoverCallbackUrl) {$true} else {$false}
}
$service.AutodiscoverUrl($Credential.UserName, $TestUrlCallback)
return $Service
}
和
function Get-ExchangeOnlineMailContent {
<#
.Synopsis
Fetches content from an Exchange Online user session, typically mail.
.DESCRIPTION
Fetches content from an Exchange Online user session, typically mail.
.EXAMPLE
$Service = Enter-ExchangeOnlineSession -Credential $CredMail -MailDomain example.com
Get-ExchangeOnlineMailContent -ServiceObject $Service -PageSize 5 -Offset 0 -PageIndexLimit 15 -WellKnownFolderName Inbox
.INPUTS
Inputs to this cmdlet (if any)
.OUTPUTS
Output from this cmdlet (if any)
.NOTES
General notes
.COMPONENT
The component this cmdlet belongs to
.ROLE
The role this cmdlet belongs to
.FUNCTIONALITY
The functionality that best describes this cmdlet
#>
Param(
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
ValueFromRemainingArguments=$false,
Position=0)]
$ServiceObject,
# Define paging as described in https://msdn.microsoft.com/en-us/library/office/dn592093(v=exchg.150).aspx
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
ValueFromRemainingArguments=$false,
Position=1)]
[int]$PageSize,
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
ValueFromRemainingArguments=$false,
Position=2)]
[int]$Offset,
#Translates into multiples of $PageSize
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
ValueFromRemainingArguments=$false,
Position=3)]
[int]$PageIndexLimit,
# WellKnownFolderNames doc https://msdn.microsoft.com/en-us/library/microsoft.exchange.webservices.data.wellknownfoldername(v=exchg.80).aspx
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
ValueFromRemainingArguments=$false,
Position=4)]
[ValidateSet('Calendar',
'Contacts',
'DeletedItems',
'Drafts',
'Inbox',
'Journal',
'Notes',
'Outbox',
'SentItems',
'Tasks',
'MsgFolderRoot',
'PublicFoldersRoot',
'Root',
'JunkEmail',
'SearchFolders',
'VoiceMail',
'RecoverableItemsRoot',
'RecoverableItemsDeletions',
'RecoverableItemsVersions',
'RecoverableItemsPurges',
'ArchiveRoot',
'ArchiveMsgFolderRoot',
'ArchiveDeletedItems',
'ArchiveRecoverableItemsRoot',
'ArchiveRecoverableItemsDeletions',
'ArchiveRecoverableItemsVersions',
'ArchiveRecoverableItemsPurges',
'SyncIssues',
'Conflicts',
'LocalFailures',
'ServerFailures',
'RecipientCache',
'QuickContacts',
'ConversationHistory',
'ToDoSearch')]
[string]$WellKnownFolderName,
[Parameter(Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
ValueFromRemainingArguments=$false,
Position=5)]
[switch]$ParseOriginalRecipient,
[Parameter(Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
ValueFromRemainingArguments=$false,
Position=6)]
[switch]$OriginalRecipientAddressOnly,
[Parameter(Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
ValueFromRemainingArguments=$false,
Position=7)]
[datetime]$MailFromDate,
[Parameter(Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
ValueFromRemainingArguments=$false,
Position=8)]
[switch]$ConsoleOutput
)
# Create Property Set to include body and header, set body to text.
$PropertySet = New-Object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
$PropertySet.RequestedBodyType = [Microsoft.Exchange.WebServices.Data.BodyType]::Text
if ($ParseOriginalRecipient)
{
$PR_TRANSPORT_MESSAGE_HEADERS = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x007D,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)
$PropertySet.add($PR_TRANSPORT_MESSAGE_HEADERS)
}
$PageIndex = 0
# Page through folder.
do
{
# Limit the view to $pagesize number of email starting at $Offset.
$PageView = New-Object Microsoft.Exchange.WebServices.Data.ItemView($PageSize,$PageIndex,$Offset)
# Get folder data.
$FindResults = $ServiceObject.FindItems([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::$WellKnownFolderName,$PageView)
foreach ($MailItem in $FindResults.Items)
{
# Load extended properties.
$MailItem.Load($propertySet)
if ($ParseOriginalRecipient)
{
# Extended properties are one string, split by linebreak then find the line beginning with 'To:', containing original recipient address before exchange aliasing replaces it.
$OriginalRecipientStringRaw = ($MailItem.ExtendedProperties.value.split([Environment]::NewLine) | Where-Object {$_ -match '^To:'}).trimstart('To:').trim()
$MailItem | Add-Member -NotePropertyName ExtendedHeader -NotePropertyValue $OriginalRecipientStringRaw
if ($OriginalRecipientAddressOnly)
{
# Removes everything but the address '[email protected]' when string has form of e.g. '"My Name" <[email protected]>'
$MailItem | Add-Member -NotePropertyName OriginalRecipient -NotePropertyValue ($OriginalRecipientStringRaw | ForEach-Object {($_.ToString() -creplace '^[^<]*<', '').trimend('>')})
}
else
{
$MailItem | Add-Member -NotePropertyName OriginalRecipient -NotePropertyValue $OriginalRecipientStringRaw
}
if ($ConsoleOutput) {write-host -ForegroundColor Cyan "$($MailItem.DateTimeReceived) | $($MailItem.OriginalRecipient) | $($MailItem.Sender)"}
}
# Output result.
$MailItem | Select-Object -Property *
}
# Increment $index to next page.
$PageIndex += $PageSize
} while (($FindResults.MoreAvailable) `
-and ($PageIndex -lt $PageIndexLimit) `
-and ($MailItem.DateTimeReceived -gt $MailFromDate)) # Do/While there are more emails to retrieve and pagelimit is not exceeded and datetimereceived is later than date.
}
然后就可以:
$Cred = Get-Credential #Example user: [email protected]
$domain = 'example.com'
# Set how many emails we want to read at a time
$PageSizeNumOfEmails = 10
$OffSet = 0
$PageIndexLimit = 2000
$MailFolder = 'Inbox'
$DeltaTimeStamp = (Get-Date).AddDays(-30) #Go how many days back?
try
{
$Session = Enter-ExchangeOnlineSession -Credential $Cred -MailDomain $domain
}
catch
{
$Message = $_.exception.message
Write-Host -ForegroundColor Yellow $Message
}
$Mails = Get-ExchangeOnlineMailContent -ServiceObject $Session `
-PageSize $PageSizeNumOfEmails `
-Offset $OffSet `
-PageIndexLimit $PageIndexLimit `
-WellKnownFolderName $MailFolder `
-ParseOriginalRecipient `
-OriginalRecipientAddressOnly `
-MailFromDate $DeltaTimeStamp | Where-Object {$_.DateTimeReceived -gt $DeltaTimeStamp} | select *
最后$Mails
按您的意愿进行处理。