Skip to content

Instantly share code, notes, and snippets.

@Maverik
Last active January 3, 2018 16:08
Show Gist options
  • Save Maverik/05223ab94115d1cc33aef7055a3efa2c to your computer and use it in GitHub Desktop.
Save Maverik/05223ab94115d1cc33aef7055a3efa2c to your computer and use it in GitHub Desktop.
Call Exchange Powershell Commands from C# using minimal module definitions only
static readonly SerializationTypeConverter Converter = new SerializationTypeConverter();
static readonly Command SelectSerializationDataCommand = new Command("Select-Object") { Parameters = { { "Property", "SerializationData" } } };
static IEnumerable<T> ExchangeSerializerCastTo<T>(IEnumerable<object> results) => results.Where(x => Converter.CanConvertFrom(x, typeof(T)))
.Select(x => Converter.ConvertFrom(x, typeof(T), CultureInfo.CurrentCulture, false))
.OfType<T>();
void Main()
{
var state = InitialSessionState.CreateDefault2();
//Exchange2010SP3.psm1 was created using `Export-PSSession -OutputModule Exchange2010SP3 -CommandName *` command
//Of course you can use it with any exchange version (provided you have compatible module definition or export another one)
//We only need this bit to able to call commands like Get-Mailbox from the session
state.ImportPSModule(new[] { Path.Combine(Path.GetDirectoryName(typeof(Mailbox).Assembly.Location), "Exchange2010SP3.psm1") });
using (var runspace = RunspaceFactory.CreateRunspace(state))
{
try
{
runspace.Open();
//Voodoo to pull error from powershell instance - otherwise things will die quietly without a trace
var errors = (runspace.SessionStateProxy.PSVariable.Get("Error").Value as ArrayList)
.OfType<ErrorRecord>()
.Select(x => x.Exception)
.ToArray();
if (errors.Length > 0)
errors.Dump("Errors while loading module", 1);
using (var pipeline = runspace.CreatePipeline())
{
//If we wanted to pull entire exchange mailbox db, you'd leave parameters empty like in powershell - not recommended of course!
pipeline.Commands.Add(new Command("Get-MailBox") { Parameters = { { "Identity", "user@domain.local" } } });
pipeline.Commands.Add(SelectSerializationDataCommand);
var results = pipeline.Invoke();
if (pipeline.HadErrors)
{
pipeline.Error.ReadToEnd()
.OfType<PSObject>()
.Select(x => x.BaseObject)
.OfType<ErrorRecord>()
.Select(x => x.Exception)
.Dump("Errors during last invoke", 1);
return;
}
ExchangeSerializerCastTo<Mailbox>(results).Dump(2);
}
}
//We *must* close the runspace no matter what else happens otherwise we're going to run out
//of runspace handles on remote server and get stuck
finally { runspace.Close(); }
}
}
<!-- This is the header of LINQPad save file actually while the .cs file is the rest of .linq file
Only broken up in here to enable syntax highlights since gist can't handle .linq files
The only key thing to remember: this is an AnyCPU query. This will not run in 32bit Linqpad.
-->
<Query Kind="Program">
<Reference Relative="ExchangeModuleLibs\Exchange2010SP3.psm1">D:\ExchangeModuleLibs\Exchange2010SP3.psm1</Reference>
<Reference Relative="ExchangeModuleLibs\Microsoft.Exchange.Data.Directory.dll">D:\ExchangeModuleLibs\Microsoft.Exchange.Data.Directory.dll</Reference>
<Reference Relative="ExchangeModuleLibs\Microsoft.Exchange.Data.dll">D:\ExchangeModuleLibs\Microsoft.Exchange.Data.dll</Reference>
<GACReference>System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35</GACReference>
<Namespace>Microsoft.Exchange.Data</Namespace>
<Namespace>Microsoft.Exchange.Data.Directory.Management</Namespace>
<Namespace>System.Globalization</Namespace>
<Namespace>System.Management.Automation</Namespace>
<Namespace>System.Management.Automation.Runspaces</Namespace>
</Query>
@qinxg
Copy link

qinxg commented Jan 3, 2018

thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment