Backup Exchange Online Mailbox

Why is it so messy to export mailbox in PST format from Exchange Online?
Anyway… simple powershell script to automate mailbox download with Compliance Search cmdlets and unifiedexporttool executable from Microsoft
😎😚

write-host "INIZIO PROCEDURA"

$password = "password"
$user_management = "username"
$Mailbox = "mailbox@domain.com"
$ExportLocation = "c:\export"

$datetime = get-date -format yyyyMMddHHmmss
$SearchName = "$($Mailbox)_$($datetime)_PST"

$pass_management = $password | convertto-securestring -asplaintext -force
$UserCredential = new-object -typename System.Management.Automation.PSCredential -argumentlist $user_management, $pass_management

write-host "Connessione a Exchange Online..."
connect-ExchangeOnline -Credential $UserCredential -ShowBanner:$false
Connect-IPPSSession -Credential $UserCredential

write-host "Creo ricerca compliance $SearchName..."
New-ComplianceSearch -Name $SearchName -ExchangeLocation $Mailbox -AllowNotFoundExchangeLocationsEnabled $true

write-host "Avvio ricerca..."
Start-ComplianceSearch -Identity $SearchName

while ((Get-ComplianceSearch $SearchName | Select-Object -ExpandProperty Status) -ne "Completed") {
	Start-Sleep -s 2
	Write-Host -NoNewline "."
}
write-host "Ricerca $SearchName completata!"

write-host "Creo ricerca export $SearchName..."
New-ComplianceSearchAction -SearchName $SearchName -Export -Format FxStream -ExchangeArchiveFormat PerUserPst -Scope BothIndexedAndUnindexedItems -EnableDedupe $true -SharePointArchiveFormat IndividualMessage -IncludeSharePointDocumentVersions $true 
Start-Sleep -s 5 

While (-Not ((Get-ChildItem -Path $($env:LOCALAPPDATA + "\Apps\2.0\") -Filter microsoft.office.client.discovery.unifiedexporttool.exe -Recurse).FullName | Where-Object{ $_ -notmatch "_none_" } | Select-Object -First 1)){
	write-host "Scarico Unified Export Tool da Azure."
	$Manifest = "https://complianceclientsdf.blob.core.windows.net/v16/Microsoft.Office.Client.Discovery.UnifiedExportTool.application"
	$ElevatePermissions = $true
	Try {
		Add-Type -AssemblyName System.Deployment

		write-host " Inizializzo installazione ClickOnce $Manifest"
		$RemoteURI = [URI]::New( $Manifest , [UriKind]::Absolute)
		if (-not  $Manifest){
			throw "Url di connessione non valido '$ConnectionUri'"
		}
		$HostingManager = New-Object System.Deployment.Application.InPlaceHostingManager -ArgumentList $RemoteURI , $False
		Register-ObjectEvent -InputObject $HostingManager -EventName GetManifestCompleted -Action { 
			new-event -SourceIdentifier "ManifestDownloadComplete"
		} | Out-Null
		Register-ObjectEvent -InputObject $HostingManager -EventName DownloadApplicationCompleted -Action { 
			new-event -SourceIdentifier "DownloadApplicationCompleted"
		} | Out-Null
		$HostingManager.GetManifestAsync()
		$event = Wait-Event -SourceIdentifier "ManifestDownloadComplete" -Timeout 15
		if ($event ) {
			$event | Remove-Event

			write-host "Manifest Clickonce scaricato"
			$HostingManager.AssertApplicationRequirements($ElevatePermissions)
			$HostingManager.DownloadApplicationAsync()
			$event = Wait-Event -SourceIdentifier "DownloadApplicationCompleted" -Timeout 60
			if ($event ) {
				$event | Remove-Event
				write-host "ClickOnce scaricato"
			}
			else {
				write-host "ClickOnce non scaricato entro 60s" -level "ERROR"
			}
		}
		else {
			Write-error "ClickOnce Manifest non scararicato in 15s" -level "ERROR"
		}
	}
	finally {
		Get-EventSubscriber|? {$_.SourceObject.ToString() -eq 'System.Deployment.Application.InPlaceHostingManager'} | Unregister-Event
	}
}


$ExportExe = ((Get-ChildItem -Path $($env:LOCALAPPDATA + "\Apps\2.0\") -Filter microsoft.office.client.discovery.unifiedexporttool.exe -Recurse).FullName | Where-Object{ $_ -notmatch "_none_" } | Select-Object -First 1)
$ExportName = $SearchName + "_Export"

$ExportDetails = Get-ComplianceSearchAction -Identity $ExportName -IncludeCredential -Details | Select-Object * 

write-host "Inizio Export $ExportName"
while ( (Get-ComplianceSearchAction -Identity $ExportName -IncludeCredential -Details).status -ne "Completed" ) {
	Start-Sleep -s 2
	Write-Host -NoNewline "."
}

$ExportDetails = Get-ComplianceSearchAction -Identity $ExportName -IncludeCredential -Details | Select-Object * 
$ExportDetails = $ExportDetails.Results.split(";")
$ExportContainerUrl = $ExportDetails[0].trimStart("Container url: ")
$ExportSasToken = $ExportDetails[1].trimStart(" SAS token: ")
$ExportEstSize = [double]($ExportDetails[18].TrimStart(" Total estimated bytes: ")).replace(".","")
$ExportTransferred = [double]($ExportDetails[20].TrimStart(" Total transferred bytes: ")).replace(".","")
$ExportProgress = $ExportDetails[22].TrimStart(" Progress: ").TrimEnd("%")
$ExportStatus = $ExportDetails[25].TrimStart(" Export status: ")

write-host "Scarico Export su: $ExportLocation\$SearchName"
$Arguments = "-name ""$SearchName""","-source ""$ExportContainerUrl""","-key ""$ExportSasToken""","-dest ""$ExportLocation""","-trace true"
Start-Process -FilePath "$ExportExe" -ArgumentList $Arguments

while(Get-Process microsoft.office.client.discovery.unifiedexporttool -ErrorAction SilentlyContinue){
	$Downloaded = Get-ChildItem $ExportLocation\$SearchName -Recurse | Measure-Object -Property Length -Sum | Select-Object -ExpandProperty Sum
	Write-Progress -Id 1 -Activity "Esportazione in corso...." -Status "Completato..." -PercentComplete $ExportProgress -ErrorAction Continue
	if ( ($Downloaded/$ExportEstSize*100) -gt "100" ) {
		$percent = "100"
	} else {
		$percent = ($Downloaded/$ExportEstSize*100)
	}
	if ("Completed" -notlike $ExportStatus){
		Write-Progress -Id 2 -Activity "Download in corso" -Status "Completato..." -PercentComplete $percent -CurrentOperation "$Downloaded/$ExportEstSize bytes scaricati." -ErrorAction Continue
	} else {
		Write-Progress -Id 2 -Activity "Download in corso" -Status "Completato..." -PercentComplete $percent -CurrentOperation "$Downloaded/$ExportTransferred bytes scaricati." -ErrorAction Continue
	}
	Start-Sleep 10
	$ExportDetails = Get-ComplianceSearchAction -Identity $ExportName -IncludeCredential -Details
	$ExportDetails = $ExportDetails.Results.split(";")
	$ExportEstSize = [double]($ExportDetails[18].TrimStart(" Total estimated bytes: ").replace(".",""))
	$ExportTransferred = [double]($ExportDetails[20].TrimStart(" Total transferred bytes: ").replace(".",""))
	$ExportProgress = ($ExportDetails[22].TrimStart(" Progress: ").TrimEnd("%")).substring(0,($ExportDetails[22].TrimStart(" Progress: ").TrimEnd("%")).IndexOf(","))
	$ExportStatus = $ExportDetails[25].TrimStart(" Export status: ")
	Write-Host -NoNewline "."

}
write-host "Download PST Completato su $ExportLocation\$SearchName"

write-host "Cancello ricerca $SearchName"
Get-ComplianceSearch -Identity $SearchName | remove-compliancesearch -confirm:$false
Disconnect-ExchangeOnline -Confirm:$false