As you know, I like challenges. I like things that take me some time to figure out and may or may not work. I recently had someone reach out and ask for some help with PowerShell. What was needed is the following:

  1. Access specific user mailbox in Exchange Online.
  2. Navigate to a particular sub-folder.
  3. Access the mail items.
  4. Retrieve all the attachments that are PDFs.
  5. Download the PDFs locally.
  6. Inspect the content in the PDFs for a specific word.
  7. Reattach the PDFs to a new email and send them.

You know that technically, you could do this manually; you could use SharePoint search, find the files, download them, and email them again, or you could do it manually using Outlook mail client. However, the request was for using PowerShell. 

Luckily, the Microsoft Graph PowerShell module is ideal for this. So, to get started, you first need to install and import the module:

Install-Module -Name Microsoft.Graph -Force
Import-Module -Name Microsoft.Graph

Next, you need to connect to the tenant. I am using an App Registration with a Certificate with the required permissions I need.

Connect-MgGraph `
	-ClientId "5584d545-45a6-3660c3031532" `
	-TenantId "5cd54330-cd94-38a0c1ed90ae" `
	-CertificateThumbprint "51F85A30512A3B8F2C9E0B8A2"

Now, you set the variables needed for it all to work:

$folderName = "Test"
$searchableWord = "Error"
$user = "user@M365.onmicrosoft.com"
$downloadFolder = "C:\PowerShell\Files\"
$pdfDownloadFolder = "C:\PowerShell\Files\PDFs\"
$userEmail = "user@M365.onmicrosoft.com"
$recipientEmail = "user@M365.onmicrosoft.com"

To retrieve the user folders from their email, you use Get-MgUserMailFolder.

$parentFolders = Get-MgUserMailFolder -UserId $user

Depending on the folder location, for example, stored underneath Inbox, you must iterate the parent to find it.

foreach ($parentFolder in $parentFolders) {
    $subFolders = Get-MgUserMailFolderChildFolder `
	-UserId $user `
	-MailFolderId $parentFolder.Id | `
		Where-Object { $_.DisplayName -eq $folderName }
    	foreach ($subFolder in $subFolders) {
        	$folderId = $subFolder.Id
   	}
}

Now, you need to retrieve the specific folder.

$specificFolder = Get-MgUserMailFolder `
	-UserId $user `
	-MailFolderId $folderId

Now you have the required folder; you now need to get all the messages saved within the folder.

$messages = Get-MgUserMailFolderMessage `
	-UserId $user `
	-MailFolderId $specificFolder.Id | `
		Where-Object {$_.HasAttachments -eq $true}

Now you have all the messages loaded from the selected folder with attachments, you can start processing them. You may know that opening PDF files is quite complicated with PowerShell and requires some 3rd Party component. For this you need to download “pdftotext” from here:

https://dl.xpdfreader.com/xpdf-tools-win-4.04.zip

The logic here is this:

  • Iterate the PDF files.
  • Download them locally.
  • Create a “*.txt” version of the PDF.
  • Inspect the “*.txt” version for the required word.
  • Move the identified PDF files to a new folder.
foreach ($message in $messages) {
	$attachments = Get-MgUserMessageAttachment `
		-UserId $user `
		-MessageId $message.Id
		
		foreach ($attachment in $attachments) {
		$attachmentExtension = [System.IO.Path]::GetExtension($attachment.Name).ToLower()
		$attachmentName = $attachment.Name.Replace($attachmentExtension, '')

$uniqueIdentifier = -join ((65..90) + (97..122) + (48..57) | Get-Random -Count 8 | `
	ForEach-Object {[char]$_})
        	$fileName = "{0}_{1}.{2}" `
			-f $attachmentName, $uniqueIdentifier, $attachmentExtension

		$filePath = Join-Path `
			-Path $downloadFolder `
			-ChildPath $fileName

		if (-not (Test-Path $filePath)) {
			Write-Host "Downloading $($attachmentExtension) to $filePath | $($attachment.Id)"
            
		try {
			if($attachmentExtension -like "*.pdf")
			{
				$contentBytes = [System.Convert]::FromBase64String($attachment.AdditionalProperties.contentBytes)
				[System.IO.File]::WriteAllBytes($filePath, $contentBytes)

				$pdfFilePath = $filePath
				$textFilePath = $pdfFilePath.Replace(".pdf", ".txt")
                   
				& .\pdftotext.exe -layout -nopgbrk $pdfFilePath $textFilePath

				$textContent = Get-Content $textFilePath -Raw
				if ($textContent -match $searchableWord) {
					$pdfDestinationPath = Join-Path -Path $pdfDownloadFolder -ChildPath $fileName
					Move-Item $pdfFilePath $pdfDestinationPath
					Write-Host "Moved $($attachment.Name) to $pdfDestinationPath"
			}
                }

                $textFiles = Get-ChildItem -Path $downloadFolder -Filter "*.txt"
                foreach ($textFile in $textFiles) {
			Remove-Item $textFile.FullName -Force
                }
            } catch {
                Write-Host "Error processing $($attachment.Name): $_"
            }
        } else {
            Write-Host "File $($attachment.Name) already exists at $filePath"
        }
    }
}

As you can see, each file uses its original name and then a unique identifier to make sure there are no duplicates.

Next, you can construct our email and attachments using the matching PDF files you moved to the folder. You can then use Send-MgUserMail to email the requested user or group.

$attachments = Get-ChildItem -Path $pdfDownloadFolder -Filter "*.pdf" | ForEach-Object {
    $pdfContentBytes = [System.IO.File]::ReadAllBytes($_.FullName)
    $pdfContentBase64 = [System.Convert]::ToBase64String($pdfContentBytes)

    @{
        "@odata.type" = "#microsoft.graph.fileAttachment"
        Name = $_.Name
        ContentType = "application/pdf"
        ContentBytes = $pdfContentBase64
    }
}

$params = @{
    Message = @{
        Subject = "PDF Files with Errors"
        Body = @{
            ContentType = "Text"
            Content = "Attached are the PDF files with errors."
        }
        ToRecipients = @(
            @{
                EmailAddress = @{
                    Address = $recipientEmail
                }
            }
        )
        Attachments = $attachments
    }
}

Send-MgUserMail -UserId $userEmail -BodyParameter $params

The generated email contains the matching attachments you moved to the PDF folder that matched the required word.

Like I said, I like a bit of challenge, which makes me think about it and wonder if I can get it to work. I am not too worried about the reason for it; more about whether it is possible, whether it works, and whether it is even worth the complexity.