Question

Is it possible to install a Transport Agent to Exchange Server within a C# programm?

Normally you create your Agent .dll, then you need to open Exchange Management Shell and execute the following commands:

Install-TransportAgent -Name "Agent Name" -TransportAgentFactory "Factory.Class.Name" -AssemblyPath "C:\Path\to\agent.dll"

and

enable-transportagent -Identity "Agent Name"

and setting the priority:

Set-TransportAgent -Identity "Agent Name" -Priority 3

How can I install the transport agent from within a C# application (either calling a PowerShell command or directly using the .NET Framework?

Was it helpful?

Solution

I found a solution using PowerShell directly from C# and calling the corresponding cmdlets:

Note: The full code is available here: https://github.com/Pro/dkim-exchange/blob/master/Src/Configuration.DkimSigner/Exchange/ExchangeServer.cs#L111 and https://github.com/Pro/dkim-exchange/blob/master/Src/Configuration.DkimSigner/Exchange/PowershellHelper.cs#L29

/// <summary>
/// Installs the transport agent by calling the corresponding PowerShell commands (Install-TransportAgent and Enable-TransportAgent).
/// The priority of the agent is set to the highest one.
/// You need to restart the MSExchangeTransport service after install.
///
/// Throws ExchangeHelperException on error.
/// </summary>
public static void installTransportAgent()
{
    using (Runspace runspace = RunspaceFactory.CreateRunspace(getPSConnectionInfo()))
    {

        runspace.Open();
        using (PowerShell powershell = PowerShell.Create())
        {
            powershell.Runspace = runspace;

            int currPriority = 0;
            Collection<PSObject> results;

            if (!isAgentInstalled())
            {
                // Install-TransportAgent -Name "Exchange DkimSigner" -TransportAgentFactory "Exchange.DkimSigner.DkimSigningRoutingAgentFactory" -AssemblyPath "$EXDIR\ExchangeDkimSigner.dll"
                powershell.AddCommand("Install-TransportAgent");
                powershell.AddParameter("Name", AGENT_NAME);
                powershell.AddParameter("TransportAgentFactory", "Exchange.DkimSigner.DkimSigningRoutingAgentFactory");
                powershell.AddParameter("AssemblyPath", System.IO.Path.Combine(AGENT_DIR, "ExchangeDkimSigner.dll"));

                results = invokePS(powershell, "Error installing Transport Agent");

                if (results.Count == 1)
                {
                    currPriority = Int32.Parse(results[0].Properties["Priority"].Value.ToString());
                }

                powershell.Commands.Clear();

                // Enable-TransportAgent -Identity "Exchange DkimSigner"
                powershell.AddCommand("Enable-TransportAgent");
                powershell.AddParameter("Identity", AGENT_NAME);

                invokePS(powershell, "Error enabling Transport Agent");
            }

            powershell.Commands.Clear();

            // Determine current maximum priority
            powershell.AddCommand("Get-TransportAgent");

            results = invokePS(powershell, "Error getting list of Transport Agents");

            int maxPrio = 0;
            foreach (PSObject result in results)
            {

                if (!result.Properties["Identity"].Value.ToString().Equals(AGENT_NAME)){
                    maxPrio = Math.Max(maxPrio, Int32.Parse(result.Properties["Priority"].Value.ToString()));
                }
            }

            powershell.Commands.Clear();

            if (currPriority != maxPrio + 1)
            {
                //Set-TransportAgent -Identity "Exchange DkimSigner" -Priority 3
                powershell.AddCommand("Set-TransportAgent");
                powershell.AddParameter("Identity", AGENT_NAME);
                powershell.AddParameter("Priority", maxPrio + 1);
                results = invokePS(powershell, "Error setting priority of Transport Agent");
            }
        }
    }

}

/// <summary>
/// Checks if the last powerShell command failed with errors.
/// If yes, this method will throw an ExchangeHelperException to notify the callee.
/// </summary>
/// <param name="powerShell">PowerShell to check for errors</param>
/// <param name="errorPrependMessage">String prepended to the exception message</param>
private static Collection<PSObject> invokePS(PowerShell powerShell, string errorPrependMessage)
{
    Collection<PSObject> results = null;
    try
    {
        results = powerShell.Invoke();
    }
    catch (System.Management.Automation.RemoteException e)
    {
        if (errorPrependMessage.Length > 0)
            throw new ExchangeHelperException("Error getting list of Transport Agents:\n" + e.Message, e);
        else
            throw e;
    }
    if (powerShell.Streams.Error.Count > 0)
    {
        string errors = errorPrependMessage;
        if (errorPrependMessage.Length > 0 && !errorPrependMessage.EndsWith(":"))
            errors += ":";

        foreach (ErrorRecord error in powerShell.Streams.Error)
        {
            if (errors.Length > 0)
                errors += "\n";
            errors += error.ToString();
        }
        throw new ExchangeHelperException(errors);
    }
    return results;
}

OTHER TIPS

Yes, Its possible to install\uninstall Exchange Transport agent using C#. In fact, i have done it.

What i did is, I called exchange cmdlets using PowerShell, and installed the agent on exchange hub server. I also had to stop\start MS Exchange Transport Agent service using C#. Besides, i also had to create the folder, where i had to place agent files, and also grant Network Service read\write permission on that folder.

Regards, Laeeq Qazi

I have written a class which allows you to run exchange commands either locally or through a remote shell.

The following commands are available in this class:

GetAgentInfo : Receive the transport agent information by passing the NAme as parameter
InstallAgent : Install a transport agent by passing Name, FactoryNAme and Assembly path
EnableAgent  : Enable a transport agent
UninstallAgent : Uninstall a transportagent

RestartTransportService : Restart the Microsoft Transport Service

It also verifies what version of Exchange is installed to load the correct SnapIn

Here is the code writte in C#:

/// <summary>
/// This class is used to connect to either an remote or the local exchange powershell and allows you to execute several exchange cmdlets easiely
/// </summary>
public class ExchangeShell : IDisposable
{
    /// <summary>
    /// registry key to verify the installed exchange version, see <see cref="verifyExchangeVersion"/> method for more info
    /// </summary>
    private string EXCHANGE_KEY = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{4934D1EA-BE46-48B1-8847-F1AF20E892C1}";
    private ExchangeVersionEnum m_ExchangeVersion;

    /// <summary>
    /// getter to receive the current exchange version (local host only)
    /// </summary>
    public ExchangeVersionEnum ExchangeVersion { get { return m_ExchangeVersion; } }

    public enum ExchangeVersionEnum {
        Unknown = 0,
        v2010 = 1,
        v2013 = 2,
    }

    public string Host { get; private set; }

    /// <summary>
    /// stores the powershell runspaces for either local or any other remote connection
    /// </summary>
    private Dictionary<string, Runspace> m_runspaces = new Dictionary<string, Runspace>();

    /// <summary>
    /// get the current runspace being used for the cmdlets - only for internal purposes
    /// </summary>
    private Runspace currentRunspace { 
        get
        {
            if (m_runspaces.ContainsKey(this.Host))
                return m_runspaces[this.Host];
            else
                throw new Exception("No Runspace found for host '" + this.Host + "'. Use SetRemoteHost first");
        } 
    }

    /// <summary>
    /// Call the constructor to either open a local exchange shell or force open the local shell as remote connection (primary used to bypass "Microsoft.Exchange.Net" assembly load failures)
    /// </summary>
    /// <param name="forceRemoteShell"></param>
    public ExchangeShell(bool forceRemoteShell = false)
    {
        if (!forceRemoteShell)
        {
            this.m_ExchangeVersion = this.verifyExchangeVersion();
            if (this.m_ExchangeVersion == ExchangeVersionEnum.Unknown) throw new Exception("Unable to verify Exchange version");

            this.SetLocalHost();
        }
        else
        {
            // Use empty hostname to connect to localhost via http://computername.domain/[...]
            this.SetRemoteHost("");
        }
    }

    /// <summary>
    /// Constructor to open a remote exchange shell
    /// TODO: display authentication prompt for different login credentials
    /// </summary>
    /// <param name="hostName">host of the remote powershell</param>
    /// <param name="authenticationPrompt">not yet implemented</param>
    public ExchangeShell(string hostName, bool authenticationPrompt = false)
    {
        // TODO: Implement prompt for authenication different then default
        this.SetRemoteHost(hostName);
    }

    /// <summary>
    /// private function to verify the exchange version (local only)
    /// </summary>
    /// <returns></returns>
    private ExchangeVersionEnum verifyExchangeVersion()
    {
        var hklm = Microsoft.Win32.Registry.LocalMachine;
        var exchangeInstall = hklm.OpenSubKey(EXCHANGE_KEY);


        if (exchangeInstall != null)
        {
            var exchangeVersionKey = exchangeInstall.GetValue("DisplayVersion").ToString();

            if (exchangeVersionKey.StartsWith("14."))
                return ExchangeVersionEnum.v2010;
            else if (exchangeVersionKey.StartsWith("15."))
                return ExchangeVersionEnum.v2013;
        }

        return ExchangeVersionEnum.Unknown;
    }

    /// <summary>
    /// set the current runspace to local.
    /// Every command will be executed on the local machine
    /// </summary>
    public void SetLocalHost()
    {
        if (!this.m_runspaces.ContainsKey("localhost"))
        {
            RunspaceConfiguration rc = RunspaceConfiguration.Create();
            PSSnapInException psSnapInException = null;
            switch (this.m_ExchangeVersion)
            {
                case ExchangeVersionEnum.v2010:
                    rc.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.E2010", out psSnapInException);
                    break;
                case ExchangeVersionEnum.v2013:
                    rc.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.SnapIn", out psSnapInException);
                    break;
            }

            if (psSnapInException != null)
                throw psSnapInException;

            var runspace = RunspaceFactory.CreateRunspace(rc);
            runspace.Open();

            this.m_runspaces.Add("localhost", runspace);
        }

        this.Host = "localhost";
    }

    /// <summary>
    /// Setup a runspace for a remote host
    /// After calling this method, currentRunspace is being used to execute the commands
    /// </summary>
    /// <param name="hostName"></param>
    public void SetRemoteHost(string hostName = null)
    {
        if (String.IsNullOrEmpty(hostName))
            hostName = Environment.MachineName + "." + Environment.UserDomainName + ".local";

        hostName = hostName.ToLower();

        if (!this.m_runspaces.ContainsKey(hostName))
        {
            WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("http://" + hostName + "/PowerShell/"), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", PSCredential.Empty);
            connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Default;
            var runspace = RunspaceFactory.CreateRunspace(connectionInfo);

            // THIS CAUSES AN ERROR WHEN USING IT IN INSTALLER
            runspace.Open();
            this.m_runspaces.Add(hostName, runspace);
        }

        this.Host = hostName;
    }

    /// <summary>
    /// Get Transport agent info
    /// </summary>
    /// <param name="Name">name of the transport agent</param>
    /// <returns></returns>
    public PSObject GetAgentInfo(string Name)
    {
        PSObject result = null;
        using (PowerShell ps = PowerShell.Create())
        {
            ps.Runspace = this.currentRunspace;

            ps.AddCommand("Get-TransportAgent");
            ps.AddParameter("Identity", Name);
            var res = ps.Invoke();
            if (res != null && res.Count > 0)
                result = res[0];
        }
        return result;
    }

    /// <summary>
    /// get a list of exchange server available in the environment
    /// </summary>
    /// <returns>collection of powershell objects containing of all available exchange server</returns>
    public ICollection<PSObject> GetExchangeServer()
    {
        ICollection<PSObject> result;
        using (PowerShell ps = PowerShell.Create())
        {
            ps.Runspace = this.currentRunspace;

            ps.AddCommand("Get-ExchangeServer");
            result = ps.Invoke();
        }
        return result;
    }

    /// <summary>
    /// Install a transport agent
    /// </summary>
    /// <param name="Name">name of the transportagent</param>
    /// <param name="AgentFactory">factory name of the transport agent</param>
    /// <param name="AssemblyPath">file path of the transport agent assembly</param>
    /// <param name="enable">if true, enable it after successfully installed</param>
    /// <returns>if true everything went ok, elsewise false</returns>
    public bool InstallAgent(string Name, string AgentFactory, string AssemblyPath, bool enable = false)
    {
        bool success = false;

        if (!System.IO.File.Exists(AssemblyPath))
            throw new Exception("Assembly '"+AssemblyPath+"' for TransportAgent '"+ Name +"' not found");

        using (PowerShell ps = PowerShell.Create())
        {
            ps.Runspace = this.currentRunspace;

            ps.AddCommand("Install-TransportAgent");
            ps.AddParameter("Name", Name);
            ps.AddParameter("TransportAgentFactory", AgentFactory);
            ps.AddParameter("AssemblyPath", AssemblyPath);
            var result = ps.Invoke();
            if (result.Count > 0)
            {
                if (enable)
                    success = this.EnableAgent(Name);
                else
                    success = true;
            }
        }
        return success;
    }

    /// <summary>
    /// Enable a transport agent
    /// </summary>
    /// <param name="Name">name of the transport agent</param>
    /// <returns>if true everything went ok, elsewise false</returns>
    public bool EnableAgent(string Name){
        bool success = false;
        using (PowerShell ps = PowerShell.Create())
        {
            ps.Runspace = this.currentRunspace;

            ps.AddCommand("Enable-TransportAgent");
            ps.AddParameter("Identity", Name);
            var result = ps.Invoke();
            if (result.Count <= 0) success = true;
        }
        return success;
    }

    /// <summary>
    /// removes a transport agent
    /// </summary>
    /// <param name="Name">name of the transport agent</param>
    /// <returns>if true everything went ok, elsewise false</returns>
    public bool UninstallAgent(string Name)
    {
        bool success = false;
        using (PowerShell ps = PowerShell.Create())
        {
            ps.Runspace = this.currentRunspace;

            ps.AddCommand("Uninstall-TransportAgent");
            ps.AddParameter("Identity", Name);
            ps.AddParameter("Confirm", false);
            var result = ps.Invoke();
            if (result.Count <= 0)success = true;
        }
        return success;
    }

    /// <summary>
    /// restart exchange transport agent service
    /// A RESTART OF THIS SERVICE REQUIRED WHEN INSTALLING A NEW AGENT
    /// </summary>
    /// <returns></returns>
    public bool RestartTransportService()
    {
        bool success = false;
        using (PowerShell ps = PowerShell.Create())
        {
            ps.Runspace = this.currentRunspace;

            ps.AddCommand("Restart-Service");
            ps.AddParameter("Name", "MSExchangeTransport");
            var result = ps.Invoke();
            if (result.Count <= 0) success = true;
        }

        return success;
    }

    public void Dispose()
    {
        if (this.m_runspaces.Count > 0)
        {
            foreach (var rs in this.m_runspaces.Values)
            {
                rs.Close();
            }

            this.m_runspaces.Clear();
        }
    }
}

Usage:

// Local
ExchangeShell shell = new ExchangeShell();
var localAgentInfo = shell.GetAgentInfo("YourAgentName");

// continue with a remote call
shell.SetRemoteHost("anotherexchangeserver.your.domain");
var remoteAgentInfo = shell.GetAgentInfo("YourAgentName");
// close all connections
shell.Dispose();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top