1

I have been working on executing a command on hundreds of remote systems with PsExec via Python 3.4.2. I have been having problems collecting the output in a parsable fashion. I've tried several variations using subprocess.Popenand os.system but neither seem to be working exactly how I need them.

As mentioned above, I need to run a command on a few hundred Windows systems. The executable that I need to run and throw arguments against could exist in one of several predictable places. I decided to take inspiration from this SO post and essentially use nested if/else statements to run the command via PsExec, collect output PsExec/Windows output, and parse output to determine success or failure (if "The system cannot find the path specified." in output:). If the command failed, then I would run the next command variation. I know this is bad practice and I'm certainly open to alternatives.

Here's my rat's nest code block:

def f_remove_ma():
    host = f_get_queue()
    g_psexec_location = "PsExec.exe"

    # Legacy 32-bit:
    ma_install_location = "<install location 1>"
    ma_arguments = "/arguments"
    output = subprocess.Popen([g_psexec_location, "\\\\" + host, "-accepteula", ma_install_location, ma_arguments], stdout=subprocess.PIPE).communicate()[0]

    if 'could not start' in output:
        print("Install not found in location 1")
        return
        # Standard 64-bit:
        ma_install_location = "<install location 2>"
        ma_arguments = "/arguments"
        output = subprocess.Popen([g_psexec_location, "\\\\" + host, "-accepteula", ma_install_location, ma_arguments], stdout=subprocess.PIPE).communicate()[0]

        if "The system cannot find the path specified." in output:
            # Standard 32-bit:
            ma_install_location = "<install location 3>"
            ma_arguments = "/arguments"
            output = subprocess.Popen([g_psexec_location, "\\\\" + host, "-accepteula", ma_install_location, ma_arguments], stdout=subprocess.PIPE).communicate()[0]

            if "The system cannot find the path specified." in output:
                # New 32/64-bit:
                ma_install_location = "<install location 4>"
                ma_arguments = "/arguments"
                output = subprocess.Popen([g_psexec_location, "\\\\" + host, "-accepteula", ma_install_location, ma_arguments], stdout=subprocess.PIPE).communicate()[0]

                if "The system cannot find the path specified." in output:
                    # New 32/64-bit:
                    ma_install_location = "<install location 5>"
                    ma_arguments = "/arguments"
                    output = subprocess.Popen([g_psexec_location, "\\\\" + host, "-accepteula", ma_install_location, ma_arguments], stdout=subprocess.PIPE).communicate()[0]

                    if "The system cannot find the path specified." in output:
                        print("No known versions of software found.")
                    else:
                        print(output)
                else:
                    print(output)
            else:
                print(output)
        else:
            print(output)
    else:
        print(output)

This method seems promising at first, but it is currently hanging for me right after PsExec execution. Below is the output.

PsExec v2.11 - Execute processes remotely
Copyright (C) 2001-2014 Mark Russinovich
Sysinternals - www.sysinternals.com

At one time I got it to actually output something, but since the output was in bytecode it wasn't parsable as a string. At the time, I tried doing .decode(output) which didn't help me any.

Here are some of the internet research I found on the topic that has helped so far:

Python Popen hanging with psexec - undesired results

Calling psexec from a python script doesn't show the whole output

Why does Popen.communicate() return b'hi\n' instead of 'hi'?

... and finally this one which isn't fully implemented in Python and doesn't seem to work for me anyways.

**Edit: Adding the feeder queue functions for "host"

def f_create_queue(ip_list):
    global g_queue
    g_queue
    for ip in ip_list:
        g_queue.put(ip)
    print("\nQueue has {} items ready for processing\n".format(g_queue.qsize()))


def f_get_queue():
    global g_queue
    return g_queue.get()

So with all of this, I have a few questions:

  1. Why is PsExec hanging after initial execution?
  2. How do I capture everything (including PsExec output) in Python?
  3. Is there a better way I can do the multiple nested If/Else statements?

Thanks in advance!

Community
  • 1
  • 1
PTW-105
  • 179
  • 1
  • 7
  • sounds like you need ansible.. http://www.ansible.com/home – qwwqwwq Mar 19 '15 at 20:44
  • is it hanging on the first call? I would also use `check_output` if you just want to capture the output – Padraic Cunningham Mar 19 '15 at 20:44
  • @PadraicCunningham - Yes, it does hang on the first call. – PTW-105 Mar 19 '15 at 20:46
  • do you get any output? Also have you tried stderr to see if you get anything there? Why are you nesting all the ifs? – Padraic Cunningham Mar 19 '15 at 20:47
  • @qwwqwwq - If I could get my company to spend money like that for a project that I'm doing because our enterprise tools aren't doing what I need, then this would be great. That simply isn't the case. :) – PTW-105 Mar 19 '15 at 20:49
  • you realise the code after the return is unreachable? – Padraic Cunningham Mar 19 '15 at 20:49
  • 1
    I would also put all the `""` in a list and iterate over it instead of multiple ifs – Padraic Cunningham Mar 19 '15 at 20:52
  • @PadraicCunningham - All I'm getting is the start of the PsExec launch. The output I'm getting is in my question above the links. Nothing appears locally on the system (as if the -accepteula switch isn't being applied). I just tried it on a system that I haven't run PsExec against before (thinking it was PsExecSvc.exe failing due to lock) to no avail. The nested if's are because of the various locations I need to try to launch the exe from. – PTW-105 Mar 19 '15 at 20:52
  • Yes but they cannot be reached, use a range loop passing each i to `.format(i)`, you can use a dict with string to check as value an the number as key. – Padraic Cunningham Mar 19 '15 at 20:53
  • @PadraicCunningham - The code being unreachable is intentional. If there's success with that command, I don't want it to try the others. It's in order of most likely -> least likely to occur. – PTW-105 Mar 19 '15 at 20:56
  • I will do up some code in a minute, you can make your code a lot more readable – Padraic Cunningham Mar 19 '15 at 20:57
  • @PTW-105. try this code http://pastebin.com/qpezCDK2 – Padraic Cunningham Mar 19 '15 at 21:03
  • @PadraicCunningham - Am I filling the strings dict with the install paths? I'm confused why it contains 4/5ths the same error message. – PTW-105 Mar 19 '15 at 21:12
  • @PTW-105. because you have the same check 4 times in your code. I could have used slightly different code but it was just simple to match the keys to the install locations. It starts at the most important and finished at the least, returning if we get a match or printing the final message if not – Padraic Cunningham Mar 19 '15 at 21:13
  • The only thing you are changing in your code is `` so you are doing a lot of repetitive things, you can split it up into two halves if you want location 1 and then the rest in a loop but I don't see much point – Padraic Cunningham Mar 19 '15 at 21:15
  • @PadraicCunningham - I definitely see where you're going with it. I need to make dicts a more frequent participant in my programing activities. I edited the code, but I get an error: "TypeError: Type str doesn't support the buffer API" – PTW-105 Mar 19 '15 at 21:18
  • @PTW-105 sorry, ansible is free, ansible tower is their new product/UI that they charge for. Its simple deployment in python, using easy to read YAML configs, over SSH, easy to deploy to many servers and collect any errors or logs, basically exactly what you are trying to implement in your question. Don't reinvent the wheel! `pip install ansible` – qwwqwwq Mar 19 '15 at 21:23
  • @PadraicCunningham - I've edited my question to include the feeder functions for "host". Ultimately, it's an IP address that's been verified as online and communicating. – PTW-105 Mar 19 '15 at 21:27
  • @qwwqwwq - I jumped to conclusions when I saw their webpage and didn't see any free options. As a novice Python programmer, I think it's probably good to reinvent the wheel for knowledge (if possible), but I'll certainly consider it for future projects! – PTW-105 Mar 19 '15 at 21:27
  • @PTW-105, use `universal_newlines=True` in the subprocess call – Padraic Cunningham Mar 19 '15 at 22:13
  • @PadraicCunningham - I've worked in that 'universal_newlines=True' into my code and massaged things around a bit more. Unfortunately, now it seems to not return any info (not even showing PsExec starting). I tossed my code into Pastebin for review (http://pastebin.com/zuw7c5X3). Thanks! – PTW-105 Mar 20 '15 at 18:44
  • are you really wnting to see if `"C:\\Program Files\\Application\\Directory\\File.exe"` etc.. is in the output? Also is there a lot of output from running the command? – Padraic Cunningham Mar 20 '15 at 18:46
  • can you also show me exactly how you would run the command from a shell – Padraic Cunningham Mar 20 '15 at 18:49
  • @PadraicCunningham - Sorry for the confusion. No, I don't expect the file path to be in the output, I need to cycle over all the potential file paths. If I see "The system cannot find the path specified." in the output it's a sign that I need to try the next potential file path. – PTW-105 Mar 20 '15 at 19:40
  • @PadraicCunningham - Here is how the command should be run from the CLI (Windows): `PsExec.exe \\hostname -accepteula "C:\Program Files (x86)\Application\Directory\File.exe" /arguments` – PTW-105 Mar 20 '15 at 20:04
  • ok, should you not be checking something other than `if "The system cannot find the path specified." not in out` instead of `if strings[i] not in out`. does removing communicate and `for line in output.stdout:print(line)` give you any output? – Padraic Cunningham Mar 20 '15 at 20:14

0 Answers0