Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #Purpose: To search, export, download and upload results from folderID script to Azure. #Created by **removed**#
- #Connect to Cloud Session
- # Connect-ExchangeOnline -UserPrincipalName **removed**@**removed**.onmicrosoft.com -ShowProgress $true
- #Connect to Compliance Session with standard non-elevated account.
- # Connect-IPPSSession -UserPrincipalName **removed** -ConnectionUri https://ps.compliance.protection.outlook.com/powershell-liveid
- ###User Assigned Variables### DO NOT USE A TRAILING BACKSLASHES ON ANY PATHS!!!
- #name of your open ediscovery case
- $case = "**removed** Open Cases"
- #folder containing folderID .txt files.
- $folder = "D:\Temp"
- #path to export PST files.
- $exportPath = "\\**removed**\pst_transfer$\**removed** Working"
- #path to folder for completed PST files.
- $completedPath = "\\**removed**\pst_transfer$\**removed** Completed"
- #path of your AZCopy folder containing AZCopy.exe
- $AZPath = "C:\Users\**removed**\Desktop\azcopy_windows_amd64_10.21.1"
- #@@@---------------------------End of User Variables---------------------------@@@#
- #initializing arrays
- $nameArr = @()
- $Result = @()
- #resetting counts
- $count = 0
- $multi = 1
- $inc = 0
- #recurse through all .txt files in folder path
- Get-ChildItem $folder\ -Filter *.txt |
- ForEach-Object {
- #get mailbox UPN from filename by removing .txt from end
- $upn = $_.Name.ToString() -replace '.txt',''
- if($upn.EndsWith(1) -or $upn.EndsWith(2) -or $upn.EndsWith(3)){
- $upn = $upn -replace ".{1}$"
- }
- #get mailbox name and address from UPN
- $mbx = get-mailbox $upn
- $name = $mbx.DisplayName
- $email = $mbx.PrimarySMTPAddress
- #input data from current .txt file, add formatting for e-discovery search.
- $IDs = Get-Content "$folder\$_"
- $IDs = $IDs | ForEach-Object {"$_ (c:s)"}
- #create and then start new e-discovery search.
- if($inc -ne 20){
- New-ComplianceSearch -Name $name$multi -Case $case -ExchangeLocation $email -ContentMatchQuery "$IDs"
- Start-ComplianceSearch $name$multi
- $multi++
- $inc++
- }else{ #after submitting 20 exports, wait 60 seconds before submitting more to avoid hitting concurrent org cap
- Write-Host "20 searches submitted. Waiting 60 seconds before submitting more to avoid limit."
- Start-Sleep -Seconds 60
- New-ComplianceSearch -Name $name$multi -Case $case -ExchangeLocation $email -ContentMatchQuery "$IDs"
- Start-ComplianceSearch $name$multi
- $inc = 0
- $multi++
- }
- }
- Write-Host ""
- $searchAction = Get-ComplianceSearch -Case $case -ResultSize unlimited | Where-Object {$_.Status -ne "Completed"}
- #loops through each in-progress search until all are complete
- While($searchAction -ne $null){
- Write-Host "Waiting for all searches to complete..."
- Start-Sleep -Seconds 15
- $searchAction = Get-ComplianceSearch -Case $case -ResultSize unlimited | Where-Object {$_.Status -ne "Completed"}
- }
- Write-Host "All searches are now ready for export!"
- Write-Host ""
- #resetting counts for naming consistency
- $multi = 1
- $inc = 0
- Write-Host "Creating exports..."
- #exporting searches
- Get-ChildItem $folder\ -Filter *.txt |
- ForEach-Object {
- #get mailbox UPN from filename by removing .txt from end
- $upn = $_.Name.ToString() -replace '.txt',''
- if($upn.EndsWith(1) -or $upn.EndsWith(2) -or $upn.EndsWith(3)){
- $upn = $upn -replace ".{1}$"
- }
- #get mailbox name from UPN
- $mbx = get-mailbox $upn
- $name = $mbx.DisplayName
- #filling processed names into array for later use
- $nameArr = $nameArr + "$name$multi"
- #create exports for each search
- if($inc -ne 20){
- New-ComplianceSearchAction -SearchName $name$multi -Export -ExchangeArchiveFormat PerUserPst -format FxStream -Confirm:$False | out-null
- $multi++
- $inc++
- }else{ #after submitting 20 exports, wait 60 seconds before submitting more to avoid hitting concurrent org cap
- Write-Host "20 exports submitted. Waiting 60 seconds before submitting more to avoid limit."
- Start-Sleep -Seconds 60
- New-ComplianceSearchAction -SearchName $name$multi -Export -ExchangeArchiveFormat PerUserPst -format FxStream -Confirm:$False | out-null
- $inc = 0
- $multi++
- }
- }
- Write-Host ""
- Write-Host "All exports have been created!"
- Write-Host ""
- #create search variable containing any non-completed exports
- $searchAction = Get-ComplianceSearchAction -Case $case -ResultSize unlimited | Where-Object {$_.Status -ne "Completed"}
- #loops through each in-progress export until all are complete
- Write-Host "(This can take a while)"
- While($searchAction -ne $null){
- Write-Host "Waiting for all exports to complete..."
- Start-Sleep -Seconds 15
- $searchAction = Get-ComplianceSearchAction -Case $case -ResultSize unlimited | Where-Object {$_.Status -ne "Completed"}
- }
- Write-Host "All exports are now ready for download!"
- Write-Host " "
- #check if unified export tool is downloaded, and download if it's not
- 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 "Downloading Unified Export Tool."
- Write-Host "This is installed per-user by the Click-Once installer."
- #url to download unified export tool
- $Manifest = "https://complianceclientsdf.blob.core.windows.net/v16/Microsoft.Office.Client.Discovery.UnifiedExportTool.application"
- $ElevatePermissions = $true
- Try{
- #adds system.deployment .NET class
- Add-Type -AssemblyName System.Deployment
- Write-Host "Starting installation of ClickOnce Application $Manifest "
- $RemoteURI = [URI]::New( $Manifest , [UriKind]::Absolute)
- if (-not $Manifest){
- throw "Invalid ConnectionUri parameter '$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 "ClickOnce Manifest Download Completed"
- $HostingManager.AssertApplicationRequirements($ElevatePermissions)
- $HostingManager.DownloadApplicationAsync()
- $event = Wait-Event -SourceIdentifier "DownloadApplicationCompleted" -Timeout 60
- if ($event ) {
- $event | Remove-Event
- Write-Host "ClickOnce Application Download Completed"
- }
- else {
- Write-error "ClickOnce Application Download did not complete in time (60s)"
- }
- }
- else {
- Write-error "ClickOnce Manifest Download did not complete in time (15s)"
- }
- }
- finally {
- Get-EventSubscriber|? {$_.SourceObject.ToString() -eq 'System.Deployment.Application.InPlaceHostingManager'} | Unregister-Event
- }
- }
- #setting path of unified export tool
- $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)
- #verify working folder is empty to avoid errors
- While(Test-Path $exportPath\*){
- Read-Host "$exportPath is not empty. Please check and remove its contents to avoid issues. Press Enter to continue"
- }
- #resetting counts and cleaning array from any previous runs
- $skipped = $null
- $count = 0
- $multi = 1
- $total = $nameArr.Count
- $map = @()
- $desktopPath = [Environment]::GetFolderPath("Desktop")
- #loop through each processed mailbox and download their PSTs
- $nameArr | ForEach-Object{
- $name = $_
- $count++
- $skip = $False
- Write-Host "Currently processing export $count/$total!"
- #gather the URL and Token from the export in order to start the download, along with other information
- $ExportName = $name + "_Export"
- $ExportDetails = Get-ComplianceSearchAction -Identity $ExportName -IncludeCredential -Details
- $ExportDetails = $ExportDetails.Results.split(";")
- $ExportContainerUrl = $ExportDetails[0].trimStart("Container url: ")
- $ExportSasToken = $ExportDetails[1].trimStart(" SAS token: ")
- $ExportEstSize = ($ExportDetails[18].TrimStart(" Total estimated bytes: ") -as [double])
- $ExportTransferred = ($ExportDetails[20].TrimStart(" Total transferred bytes: ") -as [double])
- $ExportProgress = $ExportDetails[22].TrimStart(" Progress: ").TrimEnd("%")
- $ExportStatus = $ExportDetails[25].TrimStart(" Export status: ")
- #document and remove any searches with 0 content
- if ($ExportEstSize -eq 0){
- Write-Host "$name has no content in its search, and will be removed..."
- #remove compliance search & export
- $ExportName = $name + "_Export"
- Remove-ComplianceSearchAction -Identity $ExportName -Confirm:$False
- Remove-ComplianceSearch $name -Confirm:$False
- If($?){Write-Host "Compliance search & export removed!"}
- Write-Host "_________________________________"
- Write-Host ""
- $skip = $True
- if ($skipped -eq $null){
- $skipped = @()
- }
- #populate skipped mailbox into $skipped variable for user viewing
- $skipped = $skipped + $name
- $multi++
- }
- #only run the rest if the search was not skipped above
- if ($skip -eq $False){
- #download the exported files from MS using unified export tool
- Write-Host "Compliance Search Size:" $ExportEstSize
- Write-Host "Initiating download..."
- Write-Host "Saving export to temporary hold folder."
- $heldPath = "$desktopPath\aMdtspg296mdg"
- #create temporary holding folder if it doesn't already exist
- if(Test-Path -Path $heldPath){
- }else{New-Item -Path $heldPath -ItemType Directory | out-null}
- Write-Host ""
- #defining arguments for export tool in start-process below
- $arguments = "-name ""$name""","-source ""$exportcontainerurl""","-key ""$exportsastoken""","-dest ""$heldPath""","-trace true"
- Start-Process -FilePath "$exportexe" -ArgumentList $arguments
- #do until unified export tool has started
- $started = $false
- Do {
- $status = Get-Process microsoft.office.client.discovery.unifiedexporttool -ErrorAction SilentlyContinue
- If (!($status)){
- Write-Host 'Waiting for process to start' ; Start-Sleep -Seconds 5
- }
- Else{
- Write-Host 'unified export tool has started' ; $started = $true
- }
- }Until ($started)
- #do until unified export tool stops running
- Do{
- $Finished = $False
- Write-host "Sleeping 10 seconds..."
- Start-Sleep -Seconds 10
- #get size of currently downloading .pst
- $DownloadSize = Get-Childitem "$heldPath\$name" -Recurse -Include *.pst | ForEach-Object{$_.Length}
- $DownloadSize = $DownloadSize / 1048576 | Out-String
- #catch errors caused if .pst download returns as 0, and inform user the download failed then skip the rest of the script.
- Try{
- $DownloadSize = $DownloadSize.SubString(0,6)
- }Catch{
- Write-Host "$name failed to complete export, Skipping... will need to be done manually. Check litigation hold."
- $skip = $True
- $skipped = $skipped + $name
- $Finished = $True
- }
- Write-Host " "
- Write-Host "Compliance Search Size:" $ExportEstSize
- Write-Host "Downloaded Size:" $DownloadSize "Mb"
- Write-Host " "
- #check if the downloaded size has reached expected size, then exit Do loop if export tool has been stopped as well.
- If($DownloadSize -ge $Size){
- $status = Get-Process microsoft.office.client.discovery.unifiedexporttool -ErrorAction SilentlyContinue
- If (!($status)) {
- Write-Host "Download has finished"
- $Finished = $True
- }
- }
- }until($Finished -eq $True)
- Write-Host " "
- if($skip -eq $False){
- Write-Host "PST exported & downloaded!"
- }
- #remove compliance search & export
- $ExportName = $name + "_Export"
- Remove-ComplianceSearchAction -Identity $ExportName -Confirm:$False
- Remove-ComplianceSearch $name -Confirm:$False
- If($?){Write-Host "Compliance search & export removed!"}
- Write-Host "_________________________________"
- #Ensure export processes are stopped before moving PSTs
- $status = Get-Process microsoft.office.client.discovery.unifiedexporttool -ErrorAction SilentlyContinue
- While($status){
- $status | ForEach-Object{Stop-Process -Id $status.Id -ErrorAction SilentlyContinue}
- $status = Get-Process microsoft.office.client.discovery.unifiedexporttool -ErrorAction SilentlyContinue
- }
- if($skip -eq $False){
- #rename then move PST for upload
- $pst = Get-Childitem $heldPath\$name -recurse -include *.pst | Select-Object Name
- $pst = "$pst".TrimStart("@{Name=").TrimEnd("}")
- Get-Childitem $heldPath\$name -recurse -include *.pst | Rename-Item -NewName "$pst".Replace(".pst","$multi.pst") -Force
- $files = Get-Childitem $heldPath\$name -recurse -include *.pst
- foreach($file in $files){
- Move-Item -Path $file.FullName -Destination $exportPath\ -Force
- }
- #binding pst to uploaded multi in array for mapping file accuracy
- $mapobj = New-Object -TypeName PSObject -Property $([ordered]@{
- pst = $name
- multi = $multi
- })
- $map += $mapobj
- }
- $multi++
- }
- }
- Read-Host "All PSTs have been downloaded! Please manually check '$exportPath' before proceeding to upload them via AZCopy. Press Enter to proceed"
- Write-Host ""
- #cleaning array from previous runs
- $Result = $null
- $Result = @()
- $mapskip = 0
- #filling mapping array
- Get-ChildItem $exportPath\ -Filter *.pst |
- ForEach-Object {
- #get mailbox UPN from filename by removing .txt from end
- if($mapskip -ge 1){
- $mapskip--
- return;
- }else{
- $upn = $_.Name.ToString() -replace '.pst',''
- $nonum = $upn -replace '\d+$'
- #get mailbox name from UPN
- $mbx = get-mailbox $nonum
- $name = $mbx.DisplayName
- $maphold = $map -match $name
- $mapmulti = $maphold.multi
- $runcount = $mapmulti.count
- $run = 0
- While($run -ne $runcount){
- $multihold = $mapmulti[$run]
- #populate ordered data into object
- $resultsObject = New-Object -TypeName PSObject -Property $([ordered]@{
- Workload = "Exchange"
- FilePath = $null
- Name = $mbx.PrimarySmtpAddress + "$multihold.pst"
- Mailbox = $mbx.PrimarySmtpAddress
- IsArchive = "FALSE"
- TargetRoot = "\Migrated Archive"
- ContentCodePage = $null
- SPFileContainer = $null
- SPManifestContainer = $null
- SPSiteUrl = $null
- })
- #add above ordered data into mapping array
- $Result += $ResultsObject
- $run++
- }
- $multi++
- if($runcount -ge 2){
- $mapskip = $runcount - 1
- }
- }
- }
- #create mapping CSV
- Remove-Item $desktopPath\mappingfile.csv -ErrorAction SilentlyContinue
- Write-Host "Creating mapping file for processed PSTs..."
- $Result | Export-CSV $desktopPath\mappingfile.csv -NoTypeInformation
- Write-Host "Mapping file has been created! The file is located on your desktop."
- Write-Host ""
- #upload all PSTs to blob storage via AZCopy after all have been processed
- $SAS = Read-Host "Please paste in the SAS URL from the Compliance center import"
- cd $AZPath
- .\azcopy.exe copy $exportPath\* $SAS --recursive=true
- $SAS = $null
- cd "$env:USERPROFILE"
- #move uploaded PSTs to completed folder
- Move-Item -Path $exportPath\* -Destination $completedPath\
- #stop any possible stuck export tool processes
- $status = Get-Process microsoft.office.client.discovery.unifiedexporttool -ErrorAction SilentlyContinue
- While($status){
- $status | ForEach-Object{Stop-Process -Id $status.Id -ErrorAction SilentlyContinue}
- $status = Get-Process microsoft.office.client.discovery.unifiedexporttool -ErrorAction SilentlyContinue
- }
- #remove created held folder and clear temp folder
- Remove-Item -Path $heldPath -Recurse -Force
- Remove-Item -Path $folder\*
- Write-Host ""
- #notify user of callable variables
- Write-Host "You can call the 'skipped' variable to see which archives were skipped due to having no content or failing their download."
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement