Search code examples
c#powershellemailoutlookoffice-interop

How do I create an Outlook rule in PowerShell that moves email to a folder?


I'm trying to write PowerShell code to create an Outlook rule to move an email.

Note I do not have access to the server, so *-InboxRule cmdlets like New-InboxRule aren't available.

There's something wonky going on with the COM interop between PowerShell and Outlook, because it works in C#, but the identical code in PowerShell doesn't.

C# code:

Microsoft.Office.Interop.Outlook.Application outlook = null;

try
{
    outlook = (Microsoft.Office.Interop.Outlook.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Outlook.Application");
}
catch
{
}

if (outlook == null)
{
    outlook = new Microsoft.Office.Interop.Outlook.Application();
}

var inbox = outlook.Application.Session.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderInbox);
var oMoveTarget = inbox.Folders["MoveTarget"];

// debugging
Console.WriteLine(inbox.FolderPath.ToString());
Console.WriteLine(oMoveTarget.FolderPath.ToString());

var rules = outlook.Session.DefaultStore.GetRules();
Console.WriteLine(string.Format("There are {0} rules", rules.Count));

var name = string.Format("Rule {0}", DateTime.Now.ToString("yyyyMMdd_HHmmss"));
var rule = rules.Create(name, Microsoft.Office.Interop.Outlook.OlRuleType.olRuleReceive);

// conditions
rule.Conditions.From.Recipients.Add("John Smith");
rule.Conditions.From.Recipients.ResolveAll();
rule.Conditions.From.Enabled = true;

// actions
rule.Actions.MoveToFolder.Folder = oMoveTarget;
rule.Actions.MoveToFolder.Enabled = true;
rules.Save(true);

Identical (other than language syntax) PowerShell code:

try
{
    $outlook = [Runtime.InteropServices.Marshal]::GetActiveObject("Outlook.Application");
}
catch
{
}

if ($outlook -eq $null)
{
    $outlook = New-Object -ComObject Outlook.Application
}

$inbox = $outlook.Application.Session.GetDefaultFolder([Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderInbox);
$oMoveTarget = $inbox.Folders["MoveTarget"];

# debugging
[Console]::WriteLine($inbox.FolderPath.ToString());
[Console]::WriteLine($oMoveTarget.FolderPath.ToString());

$rules = $outlook.Session.DefaultStore.GetRules();
[Console]::WriteLine([string]::Format("There are {0} rules", $rules.Count));

$name = [string]::Format("Rule {0}", [DateTime]::Now.ToString("yyyyMMdd_HHmmss"));
$rule = $rules.Create($name, [Microsoft.Office.Interop.Outlook.OlRuleType]::olRuleReceive);

# conditions
$rule.Conditions.From.Recipients.Add("John Smith");
$rule.Conditions.From.Recipients.ResolveAll();
$rule.Conditions.From.Enabled = $true;

# actions
$rule.Actions.MoveToFolder.Folder = $oMoveTarget;
$rule.Actions.MoveToFolder.Enabled = $true;
$rules.Save($true);

The PowerShell code fails with:

One or more rules cannot be saved because of invalid actions or conditions.
At line:1 char:1
+ $rules.Save($true);
+ ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (:) [], COMException
    + FullyQualifiedErrorId : System.Runtime.InteropServices.COMException
 

Upon further investigation, it's caused by this line, which isn't doing anything:

$rule.Actions.MoveToFolder.Folder = $oMoveTarget;

When I run the C# equivalent, if I immediately look at that property, it is set.
In PowerShell, it silently does nothing.


Solution

  • You can do this with:

    # actions
    $action = $rule.Actions.MoveToFolder
    $action.Enabled = $true
    
    [Microsoft.Office.Interop.Outlook.MoveOrCopyRuleAction].InvokeMember("Folder",[Reflection.BindingFlags]::SetProperty, $null, $action, $oMoveTarget)