How can I use Powershell to extract mail headers from .msg file? - email

I'm trying to write a script that reads the mail headers from a directory full of .msg files so I can later parse them via regex. I tried $MSG = Get-Content .\message.msg, which could work, but it's a pretty dirty output. Has anyone tried this? I can't seem to find a working example online.

You have a few options depending on your environment. If you are on a computer with Outlook installed you can easily do this with an Outlook com object. The problem is that the headers are not exposed by default so you have to dig for them.
$ol = New-Object -ComObject Outlook.Application
$msg = $ol.CreateItemFromTemplate("SOME\PATH\TO\A\MSG\FILE.msg")
$headers = $msg.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x007D001E")
$headers
At this point you have a text block with all of the header information in it. If you want a specific header you will need to write a regex to extract it.
You could also write a class that reads the raw content based on the specification. Or read in the raw content with powershell and write a regex to attempt to extract it.

Related

HTML email body truncation while sending email from outlook using PowerShell

I have one HTML file which i want to send from PowerShell using outlook. I have used below code however it is truncating the email body after 603 chars, hence only upper half of the html page is going as body in outllook email.
$body = Get-Content -Path .\\Output.html #this have around 1500 chars.
$Outlook = New-Object -ComObject Outlook.Application
$Mail = $Outlook.CreateItem(0)
$Mail.To = "xyz#abc.com"
$Mail.Subject = "Daily Dump Status"
$body.ToString()
$Mail.HTMLBody = $body # while coping the data only 603 char are going into body.
$Mail.Send()
[System.Runtime.Interopservices.Marshal\]::ReleaseComObject($Outlook) | Out-Null
I have also tried options like Out-String while coping but didnt work
There is a limit of data you could set for a property. In the Outlook object model string properties are limited in size depending on the information store type.
To bridge the gap you need to use a low-level API on which Outlook is based on - Extended MAPI which allows opening properties with stream for writing huge amount of data. The IMAPIProp::OpenProperty method provides access to a property through a particular interface. OpenProperty is an alternative to the IMAPIProp::GetProps and IMAPIProp::SetProps methods. When either GetProps or SetProps fails because the property is too large or too complex, call OpenProperty.
You may also take a look at the famous and popular wrapper around Extended MAPI - the Redemption library.
After spending hours, finally got the root cause of this issue. The html page which i was trying to sent as email body was the output of another powershell command using ConvertTo-Html. And the output of PowerShell command had "NUL" in it.
$Mail.HTMLBody = $body
Hence above command was coping the data till first "NUL". I identified this by opening the html page in notepad++. Issue got resolved after replacing NUL with " "

Copy File From Teams to File Server Using Powershell

I am trying to copy a xlsx file from my Teams channel to a location on a file server.
I've seen various articles on line that suggest Invoke-WebRequest "https://teams.microsoft.com/l/file/rest of URL here" -OutFile C:\Test\CricketQuiz.xlsx. While this works in terms of being able to see the file at the desired file location, I can't actually open it as I get this error:
I get the same error when I tried the approach suggested in this article https://blog.jourdant.me/post/3-ways-to-download-files-with-powershell .
$url = "https://teams.microsoft.com/l/file/rest of my URL here"
$output = "C:\Test\SportsQuiz.xlsx"
$start_time = Get-Date
$wc = New-Object System.Net.WebClient
$wc.DownloadFile($url, $output)
I'm guessing this is something relatively straightforward to resolve for those with more experience.
The problem here is that the link you've got (the Teams link) is not a direct link to the file at all - it's a link to an embedded version of the file, inside the Teams client (basically like a deep link). To -actually- download the file try the following:
from the url you've got, parse out the "objectUrl" part of the query string. As an example, I have:
https://teams.microsoft.com/l/file/[guid]?tenantId=[guid2]&fileType=xlsx&objectUrl=https%3A%2F%2F[tenantname].sharepoint.com%2Fsites%2FHR%2FShared%2520Documents%2FEmployee%2520Sentiment%2520Analysis.xlsx&serviceName=recent
you want (in my example): https%3A%2F%2F[tenantname].sharepoint.com%2Fsites%2FHR%2FShared%2520Documents%2FEmployee%2520Sentiment%2520Analysis.xlsx
then you need to querystring decode this, to get (e.g.) https://[tenantname].sharepoint.com/sites/HR/Shared%20Documents/Employee%20Sentiment%20Analysis.xlsx
finally, you should use the PnP-PowerShell module's Get-PnPFile to download the file. This itself is a few steps though:
3.1 you need to connect the session, using Connect-PnPOnline, but you also need to connect to the right "SPWeb". In this case, it would be Connect-PnPOnline https://[tenantname].sharepoint.com/sites/HR
3.1 after that you can download the file, but you need to url decode it again, to get rid of %20 and similar, something like:
Get-PnPFile -Url "/Shared Documents/Employee Sentiment Analysis.xlsx" -AsFile -Path "c:\temp\"
This will give you a copy of Employee Sentiment Analysis.xlsx (in my example) inside c:\temp
Obviously this can all be automated, like the querystring decoding, the connect-pnp credentials, etc., but hopefully this gets you on the right path.

Send emails with Powershell mailto with formated text

My current script creates (after an account modification) a .ps1 file that is send to another computer and there it is executed opening a new Gmail tab with some information hosted in several variables. I need this email to have format like bold, hyper-link, etc.
Im using the start-process 'mailto' for this but i can not find the way to give this email a format (believe me, i have tried), is this even possible?
I appreciate any insights on this.
My current script creates (after an account modification) a .ps1 file that is send to another computer and there it is executed opening a new Gmail tab with some information hosted in several variables. I need this email to have format like bold, hyper-link, etc.
Im using the start-process 'mailto' for this but i can not find the way to give this email a format (believe me, i have tried), is this even possible?
Additional information:
Code:
$outPut = 'Start-Process'
$outPut+= '"mailto:'+$userMail+"?Subject=Password Reset"+"&Body=Hi, your password is $Password"
$outPut+= '";'
$mailFile = "Path" + $user.SAM + ".ps1"
$outPut | Out-File $mailFile
So, this takes the information this way and stored it in a ps1 file, then executed, opening a new Gmail tab with proper data.
I need that some words has format, bold for the password or hyper-link for guideness link...
Regards!
You haven't provided any indication of what you are doing. But the way to send emails via PowerShell is with the Send-MailMessage cmdlet. If you are using Send-MailMessage and formatting the body of the message with HTML, you just need to make sure you are using the -BodyAsHtml argument.
Here's an example:
$html = "<body><h1>Heading</h1><p>paragraph.</p></body>"
Send-MailMessage -To "bob#fake.com" -From "me#fake.com" -Subject "Test" -Body $html -BodyAsHtml

Powershell: Get where Outlook by default stores its data files when Outlook is in a different language?

Im trying to get the folder where Outlook by default stores its data files. I know two
.....appdata/Microsoft/Outlook
.....My Documents/Outlook Files
I can get to the top one easy but the bottom one: It is translated to whatever language Outlook is it.
Is there a way I can get it, no matter the language it is?
$o=New-Object -ComObject outlook.application
$ns=$o.GetNamespace("MAPI")
$ns.DefaultStore
#or
$ns.Stores |?{$_.isdatafilestore} |select -expand filepath

Working with Word templates with Powershell

I am writing a function that is part of a much larger script that will take input from a web form, check to see if that user exists in either our AD or Linux systems, create the account if it doesn't, email the user when it's done, then create a Word document that we can print out and give them with their credentials (sans temp password), email address, and basic information about our IT services. I have been beating my head against the wall with the Word integration. There is almost ZERO Powershell documentation online for Word integration. I've been having to translate what I can from C# and VB and even half of that isn't even translateable. I've got it mostly working now but I'm having problems getting PS to put my text in the correct location in the Word template. I have a Word Template with 4 bookmarks where I am inserting the user's name, username, email address, and account expiration. The problem is, PS is placing all of the text at the same bookmark. I've found that if I put info in the script statically it will work (ie. $FillName.Text = 'John Doe') but if I use a variable it will just stick all of them at the first bookmark. Here is my code:
Function createWordDocument($fullname,$sam,$mailaddress,$Expiration)
{
$word = New-Object -ComObject "Word.application"
$doc = $word.Documents.add("C:\Users\smiths\Documents\Powershell Scripts\webformCreateUsers\welcome2.dotx")
$FillName=$doc.Bookmarks.Item("Name").Range
$FillName.Text="$fullname "
$FillUser=$doc.Bookmarks.Item("Username").Range
$FillUser.Text="$sam"
$FillMail=$doc.Bookmarks.Item("Email").Range
$FillMail.Text="$mailaddress"
$FillExpiration=$doc.Bookmarks.Item("Expiration").Range
$FillExpiration.Text="$Expiration"
$file = "C:\Users\smiths\Documents\Powershell Scripts\webformCreateUsers\test1.docx"
$doc.SaveAs([ref]$file)
$Word.Quit()
}
The function is receiving parameters that originated from a import-csv. $fullname, $sam and potentially $mailaddress have all been modified from their original inputs. #Expiration comes from the import-csv raw. Any help would be appreciated. This seems to be the most relevant info I could find and as far as I can tell I've got the same code, but It won't work for multiple bookmarks.
Ok, like I suggested you can setup a Mail Merge base that you can use to create docs for people. It does mean that you would need to output your data to a CSV file, but that is pretty trivial.
Start by setting up a test CSV with the data that you want to include. For simplicity you may want to place it with the word doc that references it. We'll call it mailmerge.csv for now, but you can name it whatever you want. Looks like Name, UserName, Email, and Expiration are the fields you would want. You can use dummy data in those fields for the time being.
Then setup your mail merge in Word, and save it someplace. We'll call it Welcome3.docx, and stash it in the same place as your last doc. Then, once it's setup to reference your CSV file, and saved, you can launch Word, open the master document, and perform the merge, then just save the file, and away you go.
I'll just use a modified version of your function which will create the CSV from the parameters provided, open the merge doc, execute the merge, save the new file, and close word. Then it'll pass a FileInfo object back so you can use that to send the email, or whatever.
Function createWordDocument($fullname,$sam,$mailaddress,$Expiration)
{
[PSCustomObject]#{Name=$fullname;Username=$sam;Email=$mailaddress;Expiration=$Expiration}|Export-Csv "C:\Users\smiths\Documents\Powershell Scripts\webformCreateUsers\mailmerge.csv" -NoTypeInformation -Force
$word = New-Object -ComObject "Word.application"
$doc = $word.Documents.Open("C:\Users\smiths\Documents\Powershell Scripts\webformCreateUsers\welcome3.dotx")
$doc.MailMerge.Execute()
$file = "C:\Users\smiths\Documents\Powershell Scripts\webformCreateUsers\$fullname.docx"
($word.documents | ?{$_.Name -Match "Letters1"}).SaveAs([ref]$file)
$Word.Quit()
[System.IO.FileInfo]$file
}
TheMadTechnician put me on the right track, but I had to do some tweaking. Here is what I wound up with:
Function createWordDocument($fullname)
{
$word = New-Object -ComObject "Word.application"
$doc = $word.Documents.Add("C:\Users\smiths\Documents\Powershell Scripts\webformCreateUsers\welcome_letter.docx")
$doc.MailMerge.Execute()
$file = "C:\Users\smiths\Documents\Powershell Scripts\webformCreateUsers\$fullname.docx"
($word.documents | ?{$_.Name -Match "Letters1"}).SaveAs([ref]$file)
$quitFormat = [Enum]::Parse([Microsoft.Office.Interop.Word.WdSaveOptions],"wdDoNotSaveChanges")
$Word.Quit([ref]$quitformat)
}
Instead of passing the arguments to the function, I had the main function create the mailmerge.csv file for me and just have the Word template connect to it. I'm still passing $fullname since that's what I'm naming the file in the end. The two major hiccups in the end were that everytime a mailmerge document file is opened, Word asks if you want to conect back to the source data. This means that when Powershell was trying to open it, Word was waiting for interaction and then PS would close it when it thought it was done. Of course, this meant that nothing got done. I found that there is a registry key that you must create to enable Word to skip the SQL Security check. for posterity's sake you must create a key here:
HKCU\Software\Microsoft\Office\14.0\Word\Options\ called SQLSecurityCheck with a DWORD value of 0. That allowed Word to properly open the template and manipulate the files. The last bit of trouble that I had was that Word was wanting to re-save the original file each time it ran and would leave a dialogue box open which would leave Word open and in memory. The last 2 lines force word to close without saving.