Advertisement
Guest User

Untitled

a guest
Mar 15th, 2024
189
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.34 KB | None | 0 0
  1. #Purpose: To search, export, download and upload results from folderID script to Azure. #Created by **removed**#
  2.  
  3. #Connect to Cloud Session
  4. # Connect-ExchangeOnline -UserPrincipalName **removed**@**removed**.onmicrosoft.com -ShowProgress $true
  5.  
  6. #Connect to Compliance Session with standard non-elevated account.
  7. # Connect-IPPSSession -UserPrincipalName **removed** -ConnectionUri https://ps.compliance.protection.outlook.com/powershell-liveid
  8.  
  9.  
  10. ###User Assigned Variables### DO NOT USE A TRAILING BACKSLASHES ON ANY PATHS!!!
  11. #name of your open ediscovery case
  12. $case = "**removed** Open Cases"
  13.  
  14. #folder containing folderID .txt files.
  15. $folder = "D:\Temp"
  16.  
  17. #path to export PST files.
  18. $exportPath = "\\**removed**\pst_transfer$\**removed** Working"
  19.  
  20. #path to folder for completed PST files.
  21. $completedPath = "\\**removed**\pst_transfer$\**removed** Completed"
  22.  
  23. #path of your AZCopy folder containing AZCopy.exe
  24. $AZPath = "C:\Users\**removed**\Desktop\azcopy_windows_amd64_10.21.1"
  25.  
  26. #@@@---------------------------End of User Variables---------------------------@@@#
  27.  
  28. #initializing arrays
  29. $nameArr = @()
  30. $Result = @()
  31. #resetting counts
  32. $count = 0
  33. $multi = 1
  34. $inc = 0
  35.  
  36. #recurse through all .txt files in folder path
  37. Get-ChildItem $folder\ -Filter *.txt |
  38. ForEach-Object {
  39. #get mailbox UPN from filename by removing .txt from end
  40. $upn = $_.Name.ToString() -replace '.txt',''
  41. if($upn.EndsWith(1) -or $upn.EndsWith(2) -or $upn.EndsWith(3)){
  42. $upn = $upn -replace ".{1}$"
  43. }
  44.  
  45. #get mailbox name and address from UPN
  46. $mbx = get-mailbox $upn
  47. $name = $mbx.DisplayName
  48. $email = $mbx.PrimarySMTPAddress
  49.  
  50. #input data from current .txt file, add formatting for e-discovery search.
  51. $IDs = Get-Content "$folder\$_"
  52. $IDs = $IDs | ForEach-Object {"$_ (c:s)"}
  53.  
  54. #create and then start new e-discovery search.
  55. if($inc -ne 20){
  56. New-ComplianceSearch -Name $name$multi -Case $case -ExchangeLocation $email -ContentMatchQuery "$IDs"
  57. Start-ComplianceSearch $name$multi
  58. $multi++
  59. $inc++
  60. }else{ #after submitting 20 exports, wait 60 seconds before submitting more to avoid hitting concurrent org cap
  61. Write-Host "20 searches submitted. Waiting 60 seconds before submitting more to avoid limit."
  62. Start-Sleep -Seconds 60
  63. New-ComplianceSearch -Name $name$multi -Case $case -ExchangeLocation $email -ContentMatchQuery "$IDs"
  64. Start-ComplianceSearch $name$multi
  65. $inc = 0
  66. $multi++
  67. }
  68. }
  69.  
  70. Write-Host ""
  71. $searchAction = Get-ComplianceSearch -Case $case -ResultSize unlimited | Where-Object {$_.Status -ne "Completed"}
  72. #loops through each in-progress search until all are complete
  73. While($searchAction -ne $null){
  74. Write-Host "Waiting for all searches to complete..."
  75. Start-Sleep -Seconds 15
  76. $searchAction = Get-ComplianceSearch -Case $case -ResultSize unlimited | Where-Object {$_.Status -ne "Completed"}
  77. }
  78. Write-Host "All searches are now ready for export!"
  79. Write-Host ""
  80.  
  81.  
  82. #resetting counts for naming consistency
  83. $multi = 1
  84. $inc = 0
  85. Write-Host "Creating exports..."
  86. #exporting searches
  87. Get-ChildItem $folder\ -Filter *.txt |
  88. ForEach-Object {
  89. #get mailbox UPN from filename by removing .txt from end
  90. $upn = $_.Name.ToString() -replace '.txt',''
  91. if($upn.EndsWith(1) -or $upn.EndsWith(2) -or $upn.EndsWith(3)){
  92. $upn = $upn -replace ".{1}$"
  93. }
  94.  
  95. #get mailbox name from UPN
  96. $mbx = get-mailbox $upn
  97. $name = $mbx.DisplayName
  98.  
  99. #filling processed names into array for later use
  100. $nameArr = $nameArr + "$name$multi"
  101.  
  102. #create exports for each search
  103. if($inc -ne 20){
  104. New-ComplianceSearchAction -SearchName $name$multi -Export -ExchangeArchiveFormat PerUserPst -format FxStream -Confirm:$False | out-null
  105. $multi++
  106. $inc++
  107. }else{ #after submitting 20 exports, wait 60 seconds before submitting more to avoid hitting concurrent org cap
  108. Write-Host "20 exports submitted. Waiting 60 seconds before submitting more to avoid limit."
  109. Start-Sleep -Seconds 60
  110. New-ComplianceSearchAction -SearchName $name$multi -Export -ExchangeArchiveFormat PerUserPst -format FxStream -Confirm:$False | out-null
  111. $inc = 0
  112. $multi++
  113. }
  114. }
  115. Write-Host ""
  116. Write-Host "All exports have been created!"
  117. Write-Host ""
  118.  
  119. #create search variable containing any non-completed exports
  120. $searchAction = Get-ComplianceSearchAction -Case $case -ResultSize unlimited | Where-Object {$_.Status -ne "Completed"}
  121. #loops through each in-progress export until all are complete
  122. Write-Host "(This can take a while)"
  123. While($searchAction -ne $null){
  124. Write-Host "Waiting for all exports to complete..."
  125. Start-Sleep -Seconds 15
  126. $searchAction = Get-ComplianceSearchAction -Case $case -ResultSize unlimited | Where-Object {$_.Status -ne "Completed"}
  127. }
  128. Write-Host "All exports are now ready for download!"
  129. Write-Host " "
  130.  
  131.  
  132.  
  133. #check if unified export tool is downloaded, and download if it's not
  134. 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)){
  135. Write-Host "Downloading Unified Export Tool."
  136. Write-Host "This is installed per-user by the Click-Once installer."
  137. #url to download unified export tool
  138. $Manifest = "https://complianceclientsdf.blob.core.windows.net/v16/Microsoft.Office.Client.Discovery.UnifiedExportTool.application"
  139. $ElevatePermissions = $true
  140. Try{
  141. #adds system.deployment .NET class
  142. Add-Type -AssemblyName System.Deployment
  143. Write-Host "Starting installation of ClickOnce Application $Manifest "
  144. $RemoteURI = [URI]::New( $Manifest , [UriKind]::Absolute)
  145. if (-not $Manifest){
  146. throw "Invalid ConnectionUri parameter '$ConnectionUri'"
  147. }
  148. $HostingManager = New-Object System.Deployment.Application.InPlaceHostingManager -ArgumentList $RemoteURI , $False
  149. Register-ObjectEvent -InputObject $HostingManager -EventName GetManifestCompleted -Action {
  150. new-event -SourceIdentifier "ManifestDownloadComplete"
  151. } | Out-Null
  152. Register-ObjectEvent -InputObject $HostingManager -EventName DownloadApplicationCompleted -Action {
  153. new-event -SourceIdentifier "DownloadApplicationCompleted"
  154. } | Out-Null
  155. $HostingManager.GetManifestAsync()
  156. $event = Wait-Event -SourceIdentifier "ManifestDownloadComplete" -Timeout 15
  157. if ($event ) {
  158. $event | Remove-Event
  159. Write-Host "ClickOnce Manifest Download Completed"
  160. $HostingManager.AssertApplicationRequirements($ElevatePermissions)
  161. $HostingManager.DownloadApplicationAsync()
  162. $event = Wait-Event -SourceIdentifier "DownloadApplicationCompleted" -Timeout 60
  163. if ($event ) {
  164. $event | Remove-Event
  165. Write-Host "ClickOnce Application Download Completed"
  166. }
  167. else {
  168. Write-error "ClickOnce Application Download did not complete in time (60s)"
  169. }
  170. }
  171. else {
  172. Write-error "ClickOnce Manifest Download did not complete in time (15s)"
  173. }
  174. }
  175. finally {
  176. Get-EventSubscriber|? {$_.SourceObject.ToString() -eq 'System.Deployment.Application.InPlaceHostingManager'} | Unregister-Event
  177. }
  178. }
  179. #setting path of unified export tool
  180. $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)
  181.  
  182.  
  183.  
  184.  
  185. #verify working folder is empty to avoid errors
  186. While(Test-Path $exportPath\*){
  187. Read-Host "$exportPath is not empty. Please check and remove its contents to avoid issues. Press Enter to continue"
  188. }
  189.  
  190. #resetting counts and cleaning array from any previous runs
  191. $skipped = $null
  192. $count = 0
  193. $multi = 1
  194. $total = $nameArr.Count
  195. $map = @()
  196. $desktopPath = [Environment]::GetFolderPath("Desktop")
  197. #loop through each processed mailbox and download their PSTs
  198. $nameArr | ForEach-Object{
  199. $name = $_
  200. $count++
  201. $skip = $False
  202. Write-Host "Currently processing export $count/$total!"
  203. #gather the URL and Token from the export in order to start the download, along with other information
  204. $ExportName = $name + "_Export"
  205. $ExportDetails = Get-ComplianceSearchAction -Identity $ExportName -IncludeCredential -Details
  206. $ExportDetails = $ExportDetails.Results.split(";")
  207. $ExportContainerUrl = $ExportDetails[0].trimStart("Container url: ")
  208. $ExportSasToken = $ExportDetails[1].trimStart(" SAS token: ")
  209. $ExportEstSize = ($ExportDetails[18].TrimStart(" Total estimated bytes: ") -as [double])
  210. $ExportTransferred = ($ExportDetails[20].TrimStart(" Total transferred bytes: ") -as [double])
  211. $ExportProgress = $ExportDetails[22].TrimStart(" Progress: ").TrimEnd("%")
  212. $ExportStatus = $ExportDetails[25].TrimStart(" Export status: ")
  213.  
  214. #document and remove any searches with 0 content
  215. if ($ExportEstSize -eq 0){
  216. Write-Host "$name has no content in its search, and will be removed..."
  217. #remove compliance search & export
  218. $ExportName = $name + "_Export"
  219. Remove-ComplianceSearchAction -Identity $ExportName -Confirm:$False
  220. Remove-ComplianceSearch $name -Confirm:$False
  221. If($?){Write-Host "Compliance search & export removed!"}
  222. Write-Host "_________________________________"
  223. Write-Host ""
  224.  
  225. $skip = $True
  226.  
  227. if ($skipped -eq $null){
  228. $skipped = @()
  229. }
  230. #populate skipped mailbox into $skipped variable for user viewing
  231. $skipped = $skipped + $name
  232. $multi++
  233. }
  234.  
  235. #only run the rest if the search was not skipped above
  236. if ($skip -eq $False){
  237. #download the exported files from MS using unified export tool
  238. Write-Host "Compliance Search Size:" $ExportEstSize
  239. Write-Host "Initiating download..."
  240. Write-Host "Saving export to temporary hold folder."
  241. $heldPath = "$desktopPath\aMdtspg296mdg"
  242. #create temporary holding folder if it doesn't already exist
  243. if(Test-Path -Path $heldPath){
  244. }else{New-Item -Path $heldPath -ItemType Directory | out-null}
  245. Write-Host ""
  246. #defining arguments for export tool in start-process below
  247. $arguments = "-name ""$name""","-source ""$exportcontainerurl""","-key ""$exportsastoken""","-dest ""$heldPath""","-trace true"
  248. Start-Process -FilePath "$exportexe" -ArgumentList $arguments
  249.  
  250. #do until unified export tool has started
  251. $started = $false
  252. Do {
  253. $status = Get-Process microsoft.office.client.discovery.unifiedexporttool -ErrorAction SilentlyContinue
  254. If (!($status)){
  255. Write-Host 'Waiting for process to start' ; Start-Sleep -Seconds 5
  256. }
  257. Else{
  258. Write-Host 'unified export tool has started' ; $started = $true
  259. }
  260. }Until ($started)
  261.  
  262. #do until unified export tool stops running
  263. Do{
  264. $Finished = $False
  265. Write-host "Sleeping 10 seconds..."
  266. Start-Sleep -Seconds 10
  267. #get size of currently downloading .pst
  268. $DownloadSize = Get-Childitem "$heldPath\$name" -Recurse -Include *.pst | ForEach-Object{$_.Length}
  269. $DownloadSize = $DownloadSize / 1048576 | Out-String
  270. #catch errors caused if .pst download returns as 0, and inform user the download failed then skip the rest of the script.
  271. Try{
  272. $DownloadSize = $DownloadSize.SubString(0,6)
  273. }Catch{
  274. Write-Host "$name failed to complete export, Skipping... will need to be done manually. Check litigation hold."
  275. $skip = $True
  276. $skipped = $skipped + $name
  277. $Finished = $True
  278. }
  279. Write-Host " "
  280. Write-Host "Compliance Search Size:" $ExportEstSize
  281. Write-Host "Downloaded Size:" $DownloadSize "Mb"
  282. Write-Host " "
  283.  
  284. #check if the downloaded size has reached expected size, then exit Do loop if export tool has been stopped as well.
  285. If($DownloadSize -ge $Size){
  286. $status = Get-Process microsoft.office.client.discovery.unifiedexporttool -ErrorAction SilentlyContinue
  287. If (!($status)) {
  288. Write-Host "Download has finished"
  289. $Finished = $True
  290. }
  291. }
  292. }until($Finished -eq $True)
  293.  
  294. Write-Host " "
  295.  
  296. if($skip -eq $False){
  297. Write-Host "PST exported & downloaded!"
  298. }
  299.  
  300. #remove compliance search & export
  301. $ExportName = $name + "_Export"
  302. Remove-ComplianceSearchAction -Identity $ExportName -Confirm:$False
  303. Remove-ComplianceSearch $name -Confirm:$False
  304. If($?){Write-Host "Compliance search & export removed!"}
  305. Write-Host "_________________________________"
  306.  
  307. #Ensure export processes are stopped before moving PSTs
  308. $status = Get-Process microsoft.office.client.discovery.unifiedexporttool -ErrorAction SilentlyContinue
  309. While($status){
  310. $status | ForEach-Object{Stop-Process -Id $status.Id -ErrorAction SilentlyContinue}
  311. $status = Get-Process microsoft.office.client.discovery.unifiedexporttool -ErrorAction SilentlyContinue
  312. }
  313.  
  314. if($skip -eq $False){
  315. #rename then move PST for upload
  316. $pst = Get-Childitem $heldPath\$name -recurse -include *.pst | Select-Object Name
  317. $pst = "$pst".TrimStart("@{Name=").TrimEnd("}")
  318. Get-Childitem $heldPath\$name -recurse -include *.pst | Rename-Item -NewName "$pst".Replace(".pst","$multi.pst") -Force
  319. $files = Get-Childitem $heldPath\$name -recurse -include *.pst
  320. foreach($file in $files){
  321. Move-Item -Path $file.FullName -Destination $exportPath\ -Force
  322. }
  323.  
  324. #binding pst to uploaded multi in array for mapping file accuracy
  325. $mapobj = New-Object -TypeName PSObject -Property $([ordered]@{
  326. pst = $name
  327. multi = $multi
  328. })
  329. $map += $mapobj
  330. }
  331. $multi++
  332. }
  333. }
  334. Read-Host "All PSTs have been downloaded! Please manually check '$exportPath' before proceeding to upload them via AZCopy. Press Enter to proceed"
  335. Write-Host ""
  336.  
  337. #cleaning array from previous runs
  338. $Result = $null
  339. $Result = @()
  340. $mapskip = 0
  341. #filling mapping array
  342. Get-ChildItem $exportPath\ -Filter *.pst |
  343. ForEach-Object {
  344. #get mailbox UPN from filename by removing .txt from end
  345. if($mapskip -ge 1){
  346. $mapskip--
  347. return;
  348. }else{
  349. $upn = $_.Name.ToString() -replace '.pst',''
  350. $nonum = $upn -replace '\d+$'
  351.  
  352. #get mailbox name from UPN
  353. $mbx = get-mailbox $nonum
  354. $name = $mbx.DisplayName
  355.  
  356. $maphold = $map -match $name
  357. $mapmulti = $maphold.multi
  358. $runcount = $mapmulti.count
  359.  
  360. $run = 0
  361. While($run -ne $runcount){
  362. $multihold = $mapmulti[$run]
  363. #populate ordered data into object
  364. $resultsObject = New-Object -TypeName PSObject -Property $([ordered]@{
  365. Workload = "Exchange"
  366. FilePath = $null
  367. Name = $mbx.PrimarySmtpAddress + "$multihold.pst"
  368. Mailbox = $mbx.PrimarySmtpAddress
  369. IsArchive = "FALSE"
  370. TargetRoot = "\Migrated Archive"
  371. ContentCodePage = $null
  372. SPFileContainer = $null
  373. SPManifestContainer = $null
  374. SPSiteUrl = $null
  375. })
  376. #add above ordered data into mapping array
  377. $Result += $ResultsObject
  378. $run++
  379. }
  380.  
  381. $multi++
  382.  
  383. if($runcount -ge 2){
  384. $mapskip = $runcount - 1
  385. }
  386. }
  387. }
  388.  
  389. #create mapping CSV
  390. Remove-Item $desktopPath\mappingfile.csv -ErrorAction SilentlyContinue
  391. Write-Host "Creating mapping file for processed PSTs..."
  392. $Result | Export-CSV $desktopPath\mappingfile.csv -NoTypeInformation
  393. Write-Host "Mapping file has been created! The file is located on your desktop."
  394. Write-Host ""
  395.  
  396.  
  397. #upload all PSTs to blob storage via AZCopy after all have been processed
  398. $SAS = Read-Host "Please paste in the SAS URL from the Compliance center import"
  399. cd $AZPath
  400. .\azcopy.exe copy $exportPath\* $SAS --recursive=true
  401. $SAS = $null
  402. cd "$env:USERPROFILE"
  403. #move uploaded PSTs to completed folder
  404. Move-Item -Path $exportPath\* -Destination $completedPath\
  405.  
  406.  
  407. #stop any possible stuck export tool processes
  408. $status = Get-Process microsoft.office.client.discovery.unifiedexporttool -ErrorAction SilentlyContinue
  409. While($status){
  410. $status | ForEach-Object{Stop-Process -Id $status.Id -ErrorAction SilentlyContinue}
  411. $status = Get-Process microsoft.office.client.discovery.unifiedexporttool -ErrorAction SilentlyContinue
  412. }
  413. #remove created held folder and clear temp folder
  414. Remove-Item -Path $heldPath -Recurse -Force
  415. Remove-Item -Path $folder\*
  416. Write-Host ""
  417.  
  418.  
  419. #notify user of callable variables
  420. Write-Host "You can call the 'skipped' variable to see which archives were skipped due to having no content or failing their download."
  421.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement