Processing Multiple Message Attachments with the Microsoft Graph PowerShell SDK
When You Want to Add Attachments to Email
Many examples exist to show how to send a single attachment with an email using the Send-MgUserMail cmdlet from the Microsoft Graph PowerShell SDK. Microsoft’s documentation covers how to send a single attachment but is mute on how to process a batch of attachments. This is understandable because the need to send multiple attachments for a single message from PowerShell isn’t probably of huge importance when it comes to programmatically creating and sending email.
With that thought in mind, I set out to create some sample code to illustrate the principle behind adding multiple attachments to a message sent with the Send-MgUserMail cmdlet.
An Array of Attachments
The essential points to remember are:
- To include one or more attachments in a message, the attachment key must be present in the hash table that describes the message. The associated value is an array of attachments.
- Each attachment is represented by a hash table in the attachments array.
- The hash table for an attachment describes its odata type, file name, content type, and the base64-encoded content for the file.
Thus, the hash table for an attachment looks like this:
$AttachmentDetails = @{ "@odata.type" = "#microsoft.graph.fileAttachment" Name = $File ContentType = $ContentType ContentBytes = $ConvertedContent }
Adding Multiple Attachments
The first step is to find some files to attach. This code looks for files in a specified folder and checks the total file size to make sure that adding all the files as attachments won’t exceed 140 MB. The documented maximum message size for Exchange Online is 150 MB, but there’s always some overhead incurred from encoding, the message body, message properties, and so on.
$AttachmentsFolder = "c:TempAttachments" [array]$InputAttachments = Get-ChildItem -Path $AttachmentsFolder If (!($InputAttachments)) { Write-Host "No attachments found in $AttachmentsFolder" Break } $FileSizeThreshold = 146800640 # 140 MB in bytes $TotalFileSize = ($InputAttachments | Measure-Object -Sum Length).Sum $FoundSizeMB = [math]::Round($TotalFileSize / 1MB, 2) If ($TotalFileSize -gt $FileSizeThreshold) { Write-Host ("Total size of attachments is {1} MB. Maximum size for an Outlook message is 140 MB. Please remove some attachments and try again." -f $TotalFileSize, $FoundSizeMB) Break }
To prevent problems, the code won’t process the attachments if their total size is more than 140 MB and will report an error like this:
Total size of attachments is 182.14 MB. Maximum size for an Outlook message is 140 MB. Please remove some attachments and try again.
This avoids the problem when an attempt is made to send a message with oversized attachments, the Send-MgUserMail cmdlet will report:
Error sending message: [ErrorMessageSizeExceeded] : The message exceeds the maximum supported size., Cannot save changes made to an item to store.
The failure could occur because the mailbox that’s sending the message isn’t capable of handling such a large email. By default, Exchange Online enterprise mailboxes can send messages of up to 150 MB and receive messages of up to 125 MB (why the two values are different is debatable). To change these values for a mailbox, run the Set-Mailbox cmdlet:
Set-Mailbox -Identity Jane.Smith@office365itpros.com -MaxReceiveSize 150MB -MaxSendSize 150MB
Populating the Attachments Array
To populate the attachments array, the code creates a base64-encoded form of the file content and attempts to figure out the most appropriate content type. This is an optional property and Microsoft 365 can decide which format is best if you omit the property.
$FullFileName = $AttachmentsFolder + "" + $File $ConvertedContent = [Convert]::ToBase64String([IO.File]::ReadAllBytes($FullFileName)) $FileExtension = [System.IO.Path]::GetExtension($FullFileName) Switch ($FileExtension) { ".pdf" { $ContentType = "application/pdf" } ".docx" { $ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document" } ".xlsx" { $ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" } ".pptx" { $ContentType = "application/vnd.openxmlformats-officedocument.presentationml.presentation" } ".jpg" { $ContentType = "image/jpeg" } ".png" { $ContentType = "image/png" } default { $ContentType = "application/octet-stream" } }
After processing an attachment, the code creates the hash table referred to earlier and adds it to the attachment array:
$MsgAttachments += $AttachmentDetails
The attachment array then becomes part of the message structure:
$Message = @{} $Message.Add('subject', $MsgSubject) $Message.Add('toRecipients', $MsgTo) $Message.Add('body', $MsgBody) $Message.Add('attachments', $MsgAttachments)
The final step is to call Send-MgUserMail to send the message. If everything works, it will arrive at its destination complete with a set of attachments (Figure 1).

Get the Structure Right and Everything Flows
Dealing with attachments through the Microsoft Graph PowerShell SDK is straightforward once you understand the structures used and how they are populated. It would be nice if the SDK cmdlet documentation covered this kind of thing better, but they don’t, so here we are.
You can download the script I used from the Office 365 for IT Pros GitHub repository. This article describes another example of using the Send-MgUserMail cmdlet.
Need some assistance to write and manage PowerShell scripts for Microsoft 365? Get a copy of the Automating Microsoft 365 with PowerShell eBook, available standalone or as part of the Office 365 for IT Pros eBook bundle.