Execute PowerShell DMT on Server Remotely

I am trying to execute a DMT PowerShell script on a remote Server from a client computer. In short, I have a program on a client computer with no Epicor client installed and I would like to kick off and execute a DMT script on the Server using the program. The Server will have the Epicor Client and DMT installed.

I can login to the remote Server and run the script correctly. Issue is when using the program, it seems to execute the script differently. It still runs. I can see that the DMT process is created and then exits but when the script is executing the Export command, it won’t write to the Target file that was specified in the Arguments when the DMT was launched. It also won’t Import the Target file either, even if I have one there.

I have launched Process Monitor on the server and filtered on CreateFile and WriteFile events for the DMT.exe process. I monitored it when it was executed locally and from the client program, the events where widely different. When it was executed locally, I can see the DMT writes to the directory that I specified in the arguments. When it is executed remotely, the DMT uses the My Documents folder for the directory and doesn’t create the actual export file. So essentially nothing happens.

The client user has admin rights(at this point) to the directory I’m using. I tested the read/write access with a simple new-item get-item script. Running it remotely will create and read those files. Other commands seem to work fine. It’s only when the DMT process is started that I see it go awry and behave differently.

Here is the basic script I am using:


$BAQ = "BAQ_Name"
$Import = "DMT Import Name Like Part"
$DMTImportOptions = "-Add -Update"
$NoUI = "-NoUI"
$User = "DMTUser"
$Pass = "DMTPassword"

$DMTPath = "C:\Epicor\ERP10.1Client\Client\DMT.exe"
$ConfigValue = "Test"


$Source = "TheExportDirectory\$BAQ.csv"
$completeLog = $Source + ".CompleteLog.txt"


$ExportArgument = "-User $User -Pass $Pass -Export -BAQ $BAQ $NoUI -ConfigValue=$ConfigValue -Target=$Source
#Write-Output $ExportArgument
$ImportArgument = "-User $User -Pass $Pass $DMTImportOptions -Import=$Import -Source  $Source $NoUI -ConfigValue=$ConfigValue "
#Write-Output $ImportArgument


try {
## Do stuff here

#Extract Data
Write-Output "$(get-date) Extracting Data via BAQ $BAQ"
Start-Process -Wait -FilePath $DMTPath -ArgumentList $ExportArgument -Verb runAs

#Load Data
Write-Output "$(get-date) Loading Data from $Source using the $Import Import" 
Start-Process -Wait -FilePath $DMTPath -ArgumentList $ImportArgument -Verb runAs


} 
catch {
    Write-Output $_.Exception.Message
}


#Check Results
try {
    ## Do stuff here
    select-string -Path $completeLog -Pattern "Records:\D*(\d+\/\d+)" -AllMatches | % { $_.Matches.Groups[0].Value }
}
catch {
    Write-Output $_.Exception.Message
}

#Check Results
try {
    ## Do stuff here
    select-string -Path $completeLog -Pattern "Errors:\D*(\d+)" -AllMatches | % { $_.Matches.Groups[0].Value }
} 
catch {
    Write-Output $_.Exception.Message
}

Here is the program that is executing the script on the client computer:

using System;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;

namespace RemoteRunspaceExecute
{

    /// <summary>
    /// This class contains the Main entry point for this host application.
    /// </summary>
    internal class RemoteRunspace02
    {
        /// <summary>
        /// This sample shows how to create a remote runspace that 
        /// runs commands on the local computer.
        /// </summary>
        /// <param name="args">Parameter not used.</param>
        private static void Main(string[] args)
        {

            string host = "remoteServer";
            string powerShellPath = "localserverpath\\Test.ps1";

            try
            {

                UriBuilder uriBuilder = new UriBuilder();
                uriBuilder.Scheme = "http";
                uriBuilder.Host = string.Format("{0}.{1}.{2}", host, System.Environment.UserDomainName, "local");
                uriBuilder.Port = 5985;

                WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri(System.IO.Path.Combine(uriBuilder.Uri.ToString(), "WSMAN")));

                connectionInfo.OperationTimeout = 4 * 60 * 1000; // 4 minutes.
                connectionInfo.OpenTimeout = 1 * 60 * 1000; // 1 minute.
                connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
                connectionInfo.ProxyAuthentication = AuthenticationMechanism.Negotiate;

                using (Runspace remoteRunspace = RunspaceFactory.CreateRunspace(connectionInfo))
                {

                    remoteRunspace.Open();

                    using (System.Management.Automation.PowerShell PowerShellInstance = System.Management.Automation.PowerShell.Create())
                    {

                        // Set the Remote Runspace
                        PowerShellInstance.Runspace = remoteRunspace;

                        // Add a script to the PowerShell object.
                        PowerShellInstance.AddScript(powerShellPath, true);

                        // Add the event handlers.  If we did not care about hooking the DataAdded
                        // event, we would let BeginInvoke create the output stream for us.
                        PSDataCollection<PSObject> output = new PSDataCollection<PSObject>();
                        output.DataAdded += new EventHandler<DataAddedEventArgs>(OnDataAdded);

                        //PowerShellInstance.Streams.Warning.DataAdded += new EventHandler<DataAddedEventArgs>(this.OnWarningDataAdded);
                        //PowerShellInstance.Streams.Verbose.DataAdded += new EventHandler<DataAddedEventArgs>(this.OnVerboseDataAdded);
                        //PowerShellInstance.Streams.Debug.DataAdded += new EventHandler<DataAddedEventArgs>(this.OnDebugDataAdded);
                        //PowerShellInstance.Streams.Error.DataAdded += new EventHandler<DataAddedEventArgs>(this.OnErrorDataAdded);

                        //PowerShellInstance.InvocationStateChanged += new EventHandler<PSInvocationStateChangedEventArgs>(this.OnInvocationStateChanged);

                        // Invoke the pipeline asynchronously.
                        IAsyncResult asyncResult = PowerShellInstance.BeginInvoke<PSObject, PSObject>(null, output);

                        // Wait for things to happen. If the user hits a key before the
                        // script has completed, then call the PowerShell Stop() method
                        // to halt processing.

                        System.Console.WriteLine("Hit any key to exit.");
                        System.Console.ReadKey();

                        if (PowerShellInstance.InvocationStateInfo.State != PSInvocationState.Completed)
                        {
                            // Stop the invocation of the pipeline.
                            Console.WriteLine("\nStopping the pipeline!\n");
                            PowerShellInstance.Stop();

                            // Wait for the Windows PowerShell state change messages to be displayed.
                            //System.Threading.Thread.Sleep(500);

                        }
                        // }


                        remoteRunspace.Close();
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            finally
            {

            }
        }
        /// <summary>
        /// The output data added event handler. This event is called when
        /// data is added to the output pipe. It reads the data that is 
        /// available and displays it on the console.
        /// </summary>
        /// <param name="sender">The output pipe this event is associated with.</param>
        /// <param name="e">Parameter is not used.</param>
        private static void OnDataAdded(object sender, DataAddedEventArgs e)
        {
            PSDataCollection<PSObject> myp = (PSDataCollection<PSObject>)sender;

            Collection<PSObject> results = myp.ReadAll();
            foreach (PSObject result in results)
            {
                if (result != null)
                {
                    Console.WriteLine(result.ToString());
                }
            }
        }
    }
}

Thanks!
Caleb

1 Like

@caleb.grundmeier

Not sure if this helps