So I have a automation that fetches attachments in from Outlook mails and does some conversion on a Virtual Machine.
We recently renewed our MSgraph secret, and since then our code has stopped working somewhat. See, some mail have a excel file directly attached, but other mails have a email attachment, wherein there is either a excel file or a zip file. We only have problems with the nested email attachments...
A function is called an that starts out with:
Write-Host "Downloading attachments:`n`tMessageId: $($mail.internetMessageId)`n`tSubject: $($mail.subject)"
$files = @()
$r_att = Invoke-GraphRequest "https://graph.microsoft.com/v1.0/me/messages/$($mail.id)/attachments" -AccessToken $token
After which the result is looped over. The if-statement handles excel files and the else handles the nested emails.
($r_att.Result.Content | ConvertFrom-Json).value | ForEach-Object {
if ($_.contentBytes) {
$f = HandleAttachment $_
if ($f) { $files += $f }
}
Next handling the nested attachment that is an excel file:
else {
Write-Host "Handling nested attachment: $($_.name)"
$v = Invoke-GraphRequest "https://graph.microsoft.com/v1.0/me/messages/$($mail.id)/attachments/$($_.id)/`$value" -AccessToken $token
$rfc822 = $v.Result.Content -replace "`r", '' -split "`n"
<#$stringbuild = $rfc822 -join "-"
return $stringbuild#>
$boundaryMatches = $rfc822 | Select-String -Pattern '^Content-Type: multipart/mixed;boundary="?(.*?)"?$' -AllMatches
if ($boundaryMatches) {
#Write-Warning "No multipart/mixed content found for nested attachment: $($_.name)"
$boundaryMatches | ForEach-Object {
$region = $_.Matches[0].Groups[1].Value
Write-Host "Found boundary: $region"
$region_start = $rfc822.IndexOf("--$region")
$region_end = $rfc822.IndexOf("--$($region)--")
if ($region_start -ne -1 -and $region_end -ne -1) {
$content = $rfc822[($region_start + 4) .. ($region_end - 1)]
$f = "$([System.IO.Path]::GetTempFileName()).xlsx"
[System.IO.File]::WriteAllBytes($f, ($content -join "`r`n").ToCharArray())
$files += $f
}
else {
Write-Warning "Could not find start or end of region for boundary: $region"
}
}
}
Next handling the nested attachment is a ZIP file.
else {
# Handle the case where the nested attachment is a zip file
$contentDisposition = $rfc822 | Select-String -Pattern '^Content-Disposition: attachment; filename=(.*\.zip)$'
if ($contentDisposition) {
$zipFileName = $contentDisposition.Matches.Groups[1].Value
Write-Host "Found zip file: $zipFileName"
# Search for the start of base64-encoded content
$base64StartLine = $rfc822 | Select-String -Pattern "^Content-Disposition: attachment; filename=$zipFileName" -CaseSensitive
if ($base64StartLine) {
Write-Host "Found start of base64-encoded content"
# Skip headers to get to the base64 content
$base64ContentLines = $rfc822[($base64StartLine.LineNumber + 1)..($rfc822.Count - 1)] |
Where-Object { $_ -notmatch "^--" } |
ForEach-Object { $_.Trim() }
# Join the lines into a single base64 string
$encodedContent = -join $base64ContentLines
Write-Host "Base64 Content Preview: $($encodedContent.Substring(0, [Math]::Min(500, $encodedContent.Length)))"
try {
$decodedContent = [Convert]::FromBase64String($encodedContent)
$zipFilePath = Join-Path $env:TEMP $zipFileName
[System.IO.File]::WriteAllBytes($zipFilePath, $decodedContent)
$files += $zipFilePath
Write-Host "Zip file saved to: $zipFilePath"
}
catch {
Write-Warning "Failed to base64-decode content for zip file: $zipFileName"
Write-Warning "Error details: $_"
}
}
else {
Write-Warning "Could not find start of base64 encoded content for zip file: $zipFileName"
}
}
else {
Write-Warning "No zip file found in nested attachment"
}
}
}
}
return $files
It seems like we cannot find a match in the $boundarMatches or the $contentDisposition. Since I have tried to do some
if($boundarymatches){
write-error "variable is set"
}
else {
write-error "variable is empty"
}
It seems impossible to try and debug in Azure Automation test pane, since I cannot get it to write output anything, so I cannot see what $rfc822 looks like.
I'll add that the runbook (test or not) will run forever (or until manually stopped) in this function and I have no idea why...
Any help would be much appreciated.
The regex for the $contentDisposition and $boundaryMatches where incorrect. Somehow the regex changed between versions.
It works now.