I have 2 Windows machines. One physical for development and another (virtual) for testing.
I want to write a PERL script that would take my compiled files and copy them to predefined locations on the virtual machine (backing up existing files).
Before copying the files I need to check for certain services and processes and kill them (start them back up at the end of the script).
I can't seem to find any real example for this on the web. Here and there I found people who use Net SCP for similar tasks but my PERL installation doesn't have and I get errors when installing it.

I was hoping to get a good direction and sound advice as to which modules I should try to use.

Thank you very much

Dani AI

Generated

Good progress, — your WMI approach is the right direction. Two important facts to keep in mind: Win32_Process.Create, when invoked remotely, cannot produce an interactive UI on the target machine, and processes started remotely are subject to the provider job object unless you clear that with the proper startup flags. Setting Win32_ProcessStartup.CreateFlags to include CREATE_NEW_CONSOLE and CREATE_BREAKAWAY_FROM_JOB is the correct fix for the short-lived process you observed. See the Create method and the Win32_ProcessStartup docs for details. (Create method)(Win32_ProcessStartup). (learn.microsoft.com)

Recommended, safe workflow (minimal changes to your Perl code):

  1. Ensure WMI/DCOM and namespace permissions (Remote Enable / Execute Method) and firewall/DCOM settings are correct on the VM. (learn.microsoft.com)
  2. Authenticate a network path and copy files to a UNC path (or use PowerShell remoting). For credentials mapping you can script net use \\VM\C$ /user:VM\User P@ss then File::Copy to the UNC path. (net use docs). (learn.microsoft.com)
  3. If you must run an interactive or long-running task on that desktop, schedule a task remotely (schtasks with /IT or create it to run as the logged-on user) or use PsExec with -i. Example (remote, run once interactively):
schtasks /Create /S REMOTEHOST /TN "DeployOneOff" /TR "C:\test.bat" /SC ONCE /ST 00:00 /RU "MyUser" /RP "p@ss" /IT /F

See schtasks docs for /IT and scheduling details. (learn.microsoft.com)

Troubleshooting tips: always pass a fully qualified path and CurrentDirectory, check the Create return code, watch Task Manager for the new PID, confirm the process account has needed network rights, and remember UAC/session-0 isolation means GUI apps won’t appear just because the PID exists. The MS WMI notes explain these remote restrictions and recommended alternatives. (learn.microsoft.com)

This is what I have come up with so far:

#! /usr/bin/perl

use warnings;
use strict;

use File::Copy;
use Win32::OLE qw(in);


use constant wbemFlagReturnImmediately => 0x10;
use constant wbemFlagForwardOnly => 0x20;

my $remotehostip = "192.168.10.102";
my $username = "myUserName";
my $password = "myPassword";
my $localdir = "c:\\localdir";
my $localfilename = "myfilename";

#1. connect to remote host
my $objWMIService = connect_to_remote_host($remotehostip, $username, $password);

#2. check (on remote host) for running service and stop it.
stop_remote_server_service($objWMIService);

#3. copy file from local machine to remote host
copy("$localdir\\$localfilename","\\\\$remotehostip\\$destinationdir") or die "Copy failed: $!";

#connect to remote host WMI service
#params: remote ip (IPv4), user name, user password
#return: remote host WMI service
sub connect_to_remote_host{
    my $remotehostip = shift(@_);
    my $username = shift(@_);
    my $password = shift(@_);
    
    #check remote ip validity (IPv4)
    die "remote IP is invalid: $remotehostip" unless $remotehostip =~ /^\d+\.\d+\.\d+\.\d+$/;
    
    my $locatorObj =Win32::OLE->new("WbemScripting.SWbemLocator") or die "ERROR CREATING OBJ";
    $locatorObj->{Security_}->{impersonationlevel} = 3;
    my $objWMIService = $locatorObj->ConnectServer($remotehostip, "root\\cimv2", $username, $password)
        or die "WMI connection failed.\n", Win32::OLE->LastError;
        
    print "connected to remote host $remotehostip\n";
    return $objWMIService;
}

#Checks for running service on remote host. Stops the service if it is running.
#params: remote WMI service
sub stop_remote_server_service{
    my $objWMIService = shift(@_);
    
    my $cpService = $objWMIService->ExecQuery("SELECT * FROM Win32_Service Where Name=\'MySercieName\'", "WQL",
              wbemFlagReturnImmediately | wbemFlagForwardOnly);
    
    #we expect only one result
    foreach my $objItem (in $cpService) {
        if ($objItem->{State} eq 'Running')
        {
            my $Result = $objItem->StopService();
            if( 0 != $Result ){
              die "Failed to stop MySercieName service on remote host";
            }
            
            print "Stopped service $objItem->{Name} on remote host\n";
        }
        
        return;
    }
    
    die "Couldn't find MyServiceName service.";
}

This works fine.

Now I'm stuck with creating a new process on the remote host. I want to run a batch file on the remote machine that performs certain tasks.
When I run the create function I get a PID but I don't see anything happening on the remote host.
Here's the code. Could really use your help here guys.

my $remotehostip = "192.168.10.102";
my $username = "myUserName";
my $password = "myPassword";
    
    my $locatorObj =Win32::OLE->new("WbemScripting.SWbemLocator") or die "ERROR CREATING OBJ";
    $locatorObj->{Security_}->{impersonationlevel} = 3; #impersonate
    my $objWMIService = $locatorObj->ConnectServer($remotehostip, "root\\cimv2", $username, $password)
        or die "WMI connection failed.\n", Win32::OLE->LastError;
        
    print "connected to remote host $remotehostip\n";
    
    #my $command_path = "c:\\test.bat";
    my $pid = Variant( VT_I4 | VT_BYREF, 0 );
    
    
    my $wmiProcessesObj = $objWMIService->Get( "Win32_Process" ) or die "Unable to get the process list:" . Win32::OLE->LastError();
    
    my $error = $wmiProcessesObj->Create($command_path,undef,undef,$pid);

    if ($error) {
       print "$command_path failed to start with WMI err $error\n", Win32::OLE->LastError;
    }else{
       print "Started $command_path pid: $pid\n";
    }

Thanks,
Gadi K

Alright. Figured out the remote process startup using WMI:

my $remotehostip = "192.168.10.102";
my $username = "myUserName";
my $password = "myPassword";
    
my $locatorObj =Win32::OLE->new("WbemScripting.SWbemLocator") or die "ERROR CREATING OBJ";
$locatorObj->{Security_}->{impersonationlevel} = 3; #impersonate
my $objWMIService = $locatorObj->ConnectServer($remotehostip, "root\\cimv2", $username, $password) or die "WMI connection failed.\n", Win32::OLE->LastError;

print "connected to remote host $remotehostip\n";
    
my $wmiProcessesObj = $objWMIService->Get( "Win32_Process" ) or die "Unable to get the process list:" . Win32::OLE->LastError();
    
my $methodObj = $wmiProcessesObj->Methods_("Create");
my $objInParams = $methodObj->{InParameters};
$objInParams->SpawnInstance_;
my $objInParamsProperties = $objInParams->{Properties_};
$objInParamsProperties->{"CommandLine"} =  "c:\\test.bat";
$objInParamsProperties->{"CurrentDirectory"} = "c:\\";
my $objOutParams = $objWMIService->ExecMethod("Win32_Process", "Create", $objInParams);
    
print "created process pid: $objOutParams->{ProcessId}";

The code I presented earlier isn't good enough. The process that is started dies after a minute or so. We can fix this by using Win32_ProcessStartup class and defining the create flags to: Create_New_Console | CREATE_BREAKAWAY_FROM_JOB.
Plus, this time I'm not using reflection:

my $remotehostip = "192.168.10.102";
my $username = "myUserName";
my $password = "myPassword";
 
my $locatorObj =Win32::OLE->new("WbemScripting.SWbemLocator") or die "ERROR CREATING OBJ";
$locatorObj->{Security_}->{impersonationlevel} = 3; #impersonate
my $objWMIService = $locatorObj->ConnectServer($remotehostip, "root\\cimv2", $username, $password)
or die "WMI connection failed.\n", Win32::OLE->LastError;
 
print "connected to remote host $remotehostip\n";

my $commandline = ""c:\\test.bat";";
my $currentdirectory = "c:\\";
my $pid = Variant( VT_I4 | VT_BYREF, 0 );
    
my $wmiProcessesObj = $objWMIService->Get( "Win32_Process" ) or die "Unable to get the process list:" . Win32::OLE->LastError();
my $Startup_Class = $objWMIService->Get("Win32_ProcessStartup");
my $Startup_Config = $Startup_Class->SpawnInstance_ ;
$Startup_Config->{CreateFlags} = 0x1000010;
    
my $error = $wmiProcessesObj->Create($commandline,$currentdirectory,$Startup_Config,$pid);

if ($error) {
       print "$commandline failed to start with WMI err $error\n", Win32::OLE->LastError;
}else{
       print "Started $commandline pid: $pid\n";
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.