Suppose you are on vacation/commute/away from your desk and get an emergency IT request. Would not it be cool to just text the PowerShell commands from your phone to your desk, have PowerShell over there execute the script, and send you back the results? 😉
Turns out this is very easy to do. All you need is Outlook, a simple rule in it, a simple PowerShell script and Outlook macro.
Here’s how this all works:
- You set up an Outlook rule to check for incoming email with a specific keyword (e.g.
$PowerShell$
) in the subject and sent from your specific email address. - You send the PowerShell script in the email body and put the keyword in the subject.
- The Outlook rule starts an Outlook script and a PowerShell script.
- The Outlook script saves the email as a text file and waits for the transcript.
- The PowerShell script executes the script exported by Outlook.
- Outlook sends the result back.
That is it!
No to the details on how to set this up!
1. Outlook script:
a. In Outlook (I am using 2007 but this should work on the previous ones just fine), click Tools/Macro/Visual Basic Editor.
b. Paste this script into the editor:
' (C) Dmitry Sotnikov
' https://dmitrysotnikov.wordpress.com
' Add this to your Outlook macros project
' Then associate SaveAsText with a rule procesing
' emails from your address with a keyword in subject
' This is to have a Sleep function in Outlook
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
' The main function saving the script email as text
' and sending back the transcript
Sub SaveAsText(MyMail As MailItem)
' Export email (with PowerShell script in body) as a text file
MyMail.SaveAs "c:\scripts\outlook.ps1", olTXT
' Create a response email
Dim reMail As Outlook.MailItem
Set reMail = MyMail.Reply
' wait till transcript is available
Set fs = CreateObject("Scripting.FileSystemObject")
While Not fs.FileExists("C:\Scripts\email_transcript.txt")
Sleep 1000
Wend
' attach the transcript and send it back
reMail.Attachments.Add "C:\Scripts\email_transcript.txt"
reMail.Send
End Sub
c. Close the Editor.
2. Create a PowerShell script which processes the script (removes the message header, executes, saves transcript). I called it execute_email.ps1 and saved to c:\scripts. Here’s the script:
# (C) Dmitry Sotnikov # https://dmitrysotnikov.wordpress.com # This is a PowerShell companion script for Outlook # macro processing PowerShell commands from email # Delete any previous transcripts and start a new one Remove-Item "c:\Scripts\email_transcript.txt" -ErrorAction SilentlyContinue Start-Transcript "c:\Scripts\email_transcript_temp.txt" # wait till Outlook saves the script email while ( -not (Test-Path "c:\Scripts\outlook.ps1")) { Start-Sleep -Seconds 1 } # Read the script, skip the header lines, execute the rest Get-Content "c:\Scripts\outlook.ps1" | Where { $i++ -gt 4 } > "c:\Scripts\justscript.ps1" . "c:\Scripts\justscript.ps1" # Remove the old script Remove-Item "c:\Scripts\outlook.ps1" -ErrorAction SilentlyContinue Remove-Item "c:\Scripts\justscript.ps1" -ErrorAction SilentlyContinue # Stop transcript and make it available for Outlook to send back Stop-Transcript Rename-Item "c:\Scripts\email_transcript_temp.txt" -NewName "email_transcript.txt"
3. Create a cmd file which starts PowerShell and executes the script. I called it execute_email.cmd
, saved to the same folder c:\scripts
and it just have one single line:
powershell.exe "c:\scripts\execute_email.ps1"
4. In Outlook click Tools/Rules and Alerts and create the rule, which executes the Outlook macro and the cmd:
You have just created a remote execution system working from any phone or internet kiosk!
Let’s test it. For example, let’s say I need to add someone to a group. I just send the script to my email address:
Outlook at my desk gets the email, saves it as text, kicks PowerShell execution, and sends me back the transcript.
Just make sure you change the keyword for something no one can guess, take your smartphone with you and go home. There’s no need to be sitting by your desk anymore. 😉
Acknowledgments: this is based on a great Lifehacker forum post on shutting down a computer based on a message. They also have posts on using other email clients such as Thunderbird or Mac Mail.app.
For your convenience I am also attaching the script files:
[UPDATE] Important: Just to make it clear: return address does not guarantee security and can be easily faked. Make sure you keep the keyword in secret or implement other means of additional protection – see one of my comments below. (So weird that Outlook does not allow to execute rules only if the email signature is verified. This could be another additional way to protect the system.)
[UPDATE 2] There’s also now a commercial alternative solution – PowerGUI Pro MobileShell – which gives in-browser PowerShell prompt from any computer or mobile device to a server in your IT environment.
Tags: PowerShell, email, hack, remoting
Utilities like Postie will let you send an email “from” any email address in the world. It’s a lot of fun to send emails to your coworkers “from” their boss, saying they’re fired. But that’s another story.
Anyway, this is prime hacking territory, and this is why you have to make absolutely sure you change that subject keyword to something no one else could possibly guess. Otherwise, if someone uses Postie to send you a PowerShell script to your email address, and they spoof the “from” address, your Outlook would run those commands with your domain account’s security. That is seriously, seriously dangerous.
Just making sure everybody understands that part.
Good point Brent. Yes, return address does not guarantee security, and you would definitely want to keep the keyword a secret or make the script a bit more complex, for example:
1. When you get the first email with the script request, reply prompting for a confirmation.
2. Only when a confirmation is received execute the actual script.
This would protect you from someone faking your address (because he or she will not get the validation request.)
you have too much time in your hand…..
It isn’t working for me. I send my powershell command in the body of the email, on my PC I get a CMD window that comes up and the email_transcript_temo.txt file states that the transcript was started, output file is …..
The powershell commands are very simple commands, like adding full permission to an AD account, etc. It doesn’t appear that outlook is tranferring the body of the email into text. I have 3 files in my c:\scripts folder
. email_transcript_temp.txt
execute_email.cmd
execute_email.ps1
Any ideas? I would love this to work. Ultimately I’m looking to be able to start and stop services on our exchange server remotely.
Thanks!
Kayla,
You need to troubleshoot your Outlook macro & rule. The macro is creating the c:\scripts\outlook.ps1 file with the body of your email which PowerShell then executes.
Looks like in your case the file does not get created – either the rule does not get started or something else happens.
Dmitry
Dmitry,
I am also having the same issue with the macro as the outlook.ps1 does not get created.
I followed your instructions for creating the macro. But I think this needs some more clarification.
I post your code into the “ThisOutlookSession” and save. However, when I set up the rule to run the script. I see two different options. Project1.ThisOutlookSession.SaveAsText & Project1.ThisOutlookSession.Sleep. I’m not sure why this happens. I assume this is because of the two different Sub calls. One for Declare & the otehr SaveAsText.
When I go to tools-> macro -> macros. I don’t see any Macros listed.
So please tell me what I am doing wrong?
Ryan,
Let me troubleshoot this later this week and get back to you…
Dmitry
Dmitry,
I am also having the same issue with the macro as the outlook.ps1 does not get created.
I have the same issues as Ryan..
Now the macro does not seem to work for me either. 😦 Probably some change of Outlook behavior introduced in a recent patch. I have posted the question to the microsoft.public.outlook.program_vba newsgroup:
http://www.microsoft.com/communities/newsgroups/list/en-us/default.aspx?dg=microsoft.public.outlook.program_vba&tid=6b45a053-27bb-4794-acd4-30e7ddd20eef&cat=en_us_6509a7cd-7779-465d-80bb-c2db5e9d38ac&lang=en&cr=us&sloc=&p=1
Let’s see what these guys have to say.
Hm… I got this fixed for me…
All I did was:
1. Going to Tools / Trust Center / Macro Security.
2. Changing the security level there to lowest (probably an overkill and needs to be properly tweaked now).
3. Restarting Outlook.
D,
I too had to lower the sec level of outlook. But I also had to change MyMail.SaveAs & MyMail.Reply to MailItem during original configuration of rule to run script. after this was defined I was able to change back in vb editor.
I also encountered another issue with being able to run the macro by itself. Here is my post and answer on another forum. http://help.wugnet.com/office/Macro-arguement-defined-ftopict1126762.html
I’ve been running this in production in conjuntion with the VMware Toolkit for several weeks. AND LOVE IT !!
Ryan,
I am glad that this was helpful!
And thanks for the additional tips!
Dmitry
This is excellent. I have a workstation that has the Exchange Management Shell install. When I run basic Powershell commands it works, when I specify Exchange commands it does not recognize it. How do I get it to use the exchange shell instead or to have basic powershell load the exchange shell to execute the commands.
Owen,
This is because Exchange cmdlet library (aka “snapin”) is not loaded by default on your computer.
To fix this, open your PowerShell profile [ https://dmitrysotnikov.wordpress.com/2008/01/25/dude-wheres-my-powershell-profile/ ] and add this line to it:
Add-PSSnapin Microsoft.Exchange.Management.PowerShell.Admin
Alternatively, you can start all your emails which have Exchange commands with this line.
Dmitry
how do you do this?
Thanks Dmitry! I had started into creating the very same solution when I ran across this article. It proved very inspirational. In the end, the script below is what I came up with.
In addition to the security concerns mentioned above, if you are emailing yourself, be sure to add an exception to your rule to ignore messages with “Re:” in the subject line or you will create an infinite email loop.
—–
Sub InvokePowershell(MyMail As MailItem)
Dim scriptName, scriptOutput, scriptCmd, rtn
scriptName = Environ(“TEMP”) & “\Invoke-Email.ps1”
scriptOutput = Environ(“TEMP”) & “\Invoke-Email_output.txt”
scriptCmd = “powershell.exe -NoLogo -NonInteractive -File “”” & scriptName & “”””
‘ Save the script to a temp file
Set fs = CreateObject(“Scripting.FileSystemObject”)
Set scriptFile = fs.CreateTextFile(scriptName, True)
scriptFile.WriteLine (“Start-Transcript ‘” & scriptOutput & “‘”)
scriptFile.WriteLine (MyMail.Body)
scriptFile.WriteLine (“Stop-Transcript”)
scriptFile.Close
Set scriptFile = Nothing
‘ Run the script
Set WshShell = CreateObject(“WScript.Shell”)
rtn = WshShell.Run(scriptCmd, 0, True)
Set WshShell = Nothing
‘ Reply with the results
Dim reMail As Outlook.MailItem
Set reMail = MyMail.Reply
If fs.FileExists(scriptOutput) = True Then
‘ Add results as attachment
‘ reMail.Body = “Output attached. RC: ” & rtn
‘ reMail.Attachments.Add scriptOutput
‘ Add to results to body
Set oTextStream = fs.OpenTextFile(scriptOutput, 1, False, -1)
reMail.Body = oTextStream.ReadAll
oTextStream.Close
Set oTextStream = Nothing
Else
reMail.Body = “No Output. RC: ” & rtn
End If
reMail.Send
Set reMail = Nothing
Set fs = Nothing
End Sub
Thanks Nathan! Also, you might want to check out PowerGUI Pro MobileShell: http://www.youtube.com/watch?v=_6QOR5nlhmk&feature=PlayList&p=807CCBBC67873456&index=18 – it works in some mobile browsers such as iPhone for example.
I would LOVE to use PowerGUI Pro, but do not have HTTP access to our network. I have plans to set it up for internal use though.
Nathan,
If VPN is not an option, there is a bunch of solutions you can use for secure “reverse proxy” http access from the internet. For example, I know that Quest has a tool for that (WebThority). I guess I need to find time to blog about that at some point… I will – just need to set up the lab and make the screenshots, and so on…
Dmitry
that’s freaking awesome! thanks, now I can rest on the Bahama’s beach
and maintain system remotely from my android phone 🙂
Any idea how to remotely login to a workstation if i will be restarted
during power break etc 😉
Here is the condensed idiot’s guide (that I wrote for myself and emailed to myself so I wouldn’t have to suffer through re-creating this feature again):
Outlook, open “Developer” tab -> Visual Basic -> ThisOutlookSession.
If “Developer” tab is not present, customize your outlook bar and add the tab.
Paste this code below into code body window that appears when you double-click the “ThisOutlookSession” object:
‘——————————————————- BEGIN SCRIPT ——————————————————-
Sub InvokePowershell(MyMail As MailItem)
Dim scriptName, scriptOutput, scriptCmd, rtn
scriptName = Environ(“TEMP”) & “\Invoke-Email.ps1”
scriptOutput = Environ(“TEMP”) & “\Invoke-Email_output.txt”
scriptCmd = “powershell.exe -NoLogo -NonInteractive -File “”” & scriptName & “”””
‘ Save the script to a temp file
Set fs = CreateObject(“Scripting.FileSystemObject”)
Set scriptFile = fs.CreateTextFile(scriptName, True)
scriptFile.WriteLine (“Start-Transcript ‘” & scriptOutput & “‘”)
scriptFile.WriteLine (MyMail.Body)
scriptFile.WriteLine (“Stop-Transcript”)
scriptFile.Close
Set scriptFile = Nothing
‘ Run the script
Set WshShell = CreateObject(“WScript.Shell”)
rtn = WshShell.Run(scriptCmd, 0, True)
Set WshShell = Nothing
‘ Reply with the results
Dim reMail As Outlook.MailItem
Set reMail = MyMail.Reply
If fs.FileExists(scriptOutput) = True Then
‘ Add results as attachment
‘ reMail.Body = “Output attached. RC: ” & rtn”
‘ reMail.Attachments.Add scriptOutput
‘ Add to results to body
Set oTextStream = fs.OpenTextFile(scriptOutput, 1, False, -1)
reMail.Body = oTextStream.ReadAll
reMail.Subject = “Script Results”
oTextStream.Close
Set oTextStream = Nothing
Else
reMail.Subject = “Script Failure”
reMail.Body = “No Output. RC: ” & rtn
End If
reMail.Send
End Sub
‘——————————————————- END SCRIPT ——————————————————-
Create out look rule to “Run a Script”. Your “InvokePowershell” script (the name of the script you just created) should appear in the dropdown list in the script selection popup window.
Create an Outlook rule:
(x) on this computer only
(x) with specific words in the subject boy: $secretword$
(x) from people or public group:
(x) run a script
(x) except if the subject or body contains specific words: “re:” or “fw:”
For some reason this line (near the bottom) didn’t show up properly in my previous post. Will retry with different formatting:
x) from people or public group: myemail@email.com
I gave this a shot with a simple command, Get-date, but got an error on the Outlook script here:
Sub SaveAsText(MyMail As MailItem)
Compile error: sub or function not defined
Any ideas?
Can’t get the VBA script part to work. I’m using Outlook 365 and macro is allowed in Trust Center.
OK, so if I just use the following VBA code, it works:
Sub SaveAsText(MyMail As MailItem)
‘ Export email (with PowerShell script in body) as a text file
MyMail.SaveAs “c:\scripts\outlook.ps1”, olTXT
End Sub
So obviously, the code just needs to be tweaked to make it work in the newer version of Outlook. Problem is, I’m not a programmer and don’t know any VBA code.
Figured it out. Here’s the updated VBA code. Issue is with the SLEEP part. I’ve replaced it with WAIT instead, see here – https://exceltrick.com/formulas_macros/vba-wait-and-sleep-functions/
Sub SaveAsText2(MyMail As MailItem)
‘ Export email (with PowerShell script in body) as a text file
MyMail.SaveAs “c:\scripts\outlook.ps1”, olTXT
‘ Create a response email
Dim reMail As Outlook.MailItem
Set reMail = MyMail.Reply
‘ wait till transcript is available
Set fs = CreateObject(“Scripting.FileSystemObject”)
While Not fs.FileExists(“C:\Scripts\email_transcript.txt”)
Application.Wait (Now + TimeValue(“0:00:01”))
Wend
‘ attach the transcript and send it back
reMail.Attachments.Add “C:\Scripts\email_transcript.txt”
reMail.Send
End Sub
Also need to fix up the Powershell script. The first 4 lines returned in the outlook.ps1 file will always include your email fields “From”, “Sent”, “To”, “Subject”. There won’t be a CC or BCC field because the presumption here is that you’ll only ever be sending this to yourself.
This means you can assume it’ll always be OK to exclude the first 4 lines, no more, no less. However, you also need to exclude the signature in your email, otherwise they will be run in the Powershell script and will error out. So, the modified version now looks something like this:
Remove-Item “c:\Scripts\email_transcript.txt” -ErrorAction SilentlyContinue
Start-Transcript “c:\Scripts\email_transcript_temp.txt”
# wait till Outlook saves the script email
while ( -not (Test-Path “c:\Scripts\outlook.ps1”)) {
Start-Sleep -Seconds 1
}
# Read the script, skip the header lines, execute the rest
(Get-Content “c:\Scripts\outlook.ps1”) -replace “Bob Lastname” -replace “Blah title” -replace “Bob.lastname@gmail.com” -replace “\+64 22 555 5555” -replace “some random text” | select-object -skip 4 > “c:\Scripts\justscript.ps1”
. “c:\Scripts\justscript.ps1”
# Remove the old script
Remove-Item “c:\Scripts\outlook.ps1” -ErrorAction SilentlyContinue
Remove-Item “c:\Scripts\justscript.ps1” -ErrorAction SilentlyContinue
# Stop transcript and make it available for Outlook to send back
Stop-Transcript
Rename-Item “c:\Scripts\email_transcript_temp.txt” -NewName “email_transcript.txt”