Search code examples
azurepowershellmicrosoft-graph-apiazure-automation

MSgraph cannot find string in attachment value using Azure Automation


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.


Solution

  • The regex for the $contentDisposition and $boundaryMatches where incorrect. Somehow the regex changed between versions.

    It works now.