I'm trying to use the MS Graph cmdlet Send-MgUserMail
to send an email and include a CSV attachment.
If I do it like this, it works fine, and the attachment is fine.
Sample CSV attachment
"App","AppId","Date","User"
"App1","43a9087d-8551-47d5-9869-3287736ce7c3","2023/01/26","[email protected]"
"App2","f33750cd-c79b-43be-8e55-ee14d163e2b3","2024/01/26","[email protected]"
Working code
$attachment = "C:\Users\Me\Downloads\SampleFile.csv"
$base64string = [Convert]::ToBase64String([IO.File]::ReadAllBytes($attachment))
$params = @{
UserId = "[email protected]"
BodyParameter = @{
Message = @{
Subject = "Test Email"
Body = @{
ContentType = "Html"
Content = "This is sample text."
}
ToRecipients = @(
@{
EmailAddress = @{
Address = "[email protected]"
}
}
)
Attachments = @(
@{
"@odata.type" = "#microsoft.graph.fileAttachment"
Name = "SampleFile.csv"
ContentType = "text/csv"
ContentBytes = $base64string
}
)
}
SaveToSentItems = "false"
}
}
Send-MgUserMail @params
Objective
What I would like is to attach the CSV, without having an actual saved file. The content would be from memory, like so.
$output = @(
[PSCustomObject] @{
App = "App1"
AppId = "43a9087d-8551-47d5-9869-3287736ce7c3"
Date = "2023/01/26"
User = "[email protected]"
},
[PSCustomObject] @{
App = "App2"
AppId = "f33750cd-c79b-43be-8e55-ee14d163e2b3"
Date = "2024/01/26"
User = "[email protected]"
}
)
$attachment = $output | ConvertTo-Csv -NoTypeInformation
$bytes = [System.Text.Encoding]::UTF8.GetBytes($attachment)
$base64string = [Convert]::ToBase64String($bytes)
My issue when I do this is the CSV content is entirely on 1 line, there's no more line breaks.
Issue Sample CSV Output
"App","AppId","Date","User" "App1","43a9087d-8551-47d5-9869-3287736ce7c3","2023/01/26","[email protected]" "App2","f33750cd-c79b-43be-8e55-ee14d163e2b3","2024/01/26","[email protected]"
How can I convert this properly?
CodePudding user response:
Answer was given in comments but to give it closure, what happens is that ConvertTo-Csv
outputs an array of strings and they don't have new line characters (CRLF in Windows, LF in Linux), before getting the bytes of your Csv you need to convert this array into a single multi-line string, for this you can use Out-String
:
$attachment = $output | ConvertTo-Csv -NoTypeInformation | Out-String
Or join them by a new line character to preserve the structure:
$attachment = ($output | ConvertTo-Csv -NoTypeInformation) -join [Environment]::NewLine
Also, [System.Text.Encoding]::UTF8.GetBytes(...)
which has a string type argument is coercing your array into a string and when you coerce an array into a string in PowerShell, it is joined by $OFS (a white space character by default) hence why you get a single line joined by whitespaces when decoding the base64 string.
Another way this could've been done is by assigning $OFS
the value of a new line character:
$attachment = $output | ConvertTo-Csv -NoTypeInformation
$OFS = [System.Environment]::NewLine
$bytes = [System.Text.Encoding]::UTF8.GetBytes($attachment)
$base64string = [Convert]::ToBase64String($bytes)
$OFS = ' '
# Decoding should look as expected
[System.Text.Encoding]::UTF8.GetString(
[Convert]::FromBase64String($base64string)
)