rundeck unable to execute powershell script with import-module - python

I am running several python scripts via rundeck (in-line) on windows 2012 target node. These scripts used to run locally but now in the process of moving to rundeck.
One of the python scripts opens subprocess to invoke a powershell script and read the output.
import subprocess
CMD = [r'C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe ', '-File', r'C:\Users\Osman\Code\mop.ps1']
cmd = CMD[:]
cmd.append('arg1')
cmd.append('arg2')
cmd.append('arg3')
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
r = p.communicate()
print(r)
The mop.ps1 is
Import-Module MSOnline
$domain = $args[0]
$login = $args[1]
$pass = $args[2]
$EPass = $pass | ConvertTo-SecureString -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PsCredential($login, $EPass)
Connect-MsolService -Credential $Cred
$TenantId = Get-MsolPartnerContract -Domain $domain | Select-Object -ExpandProperty TenantId
Get-MsolAccountSKU -TenantId $TenantId | select SkuPartNumber,ActiveUnits,ConsumedUnits | ConvertTo-Csv -NoTypeInformation
This part of the code always fails to execute and if I check stderr it says:
Connect-MsolService : Exception of type 'Microsoft.Online.Administration.Automation.MicrosoftOnlineException' was thrown.
At C:\Users\Osman\Code\mop.ps1:7 char:1
+ Connect-MsolService -Credential $Cred
I'm not sure why it fails. I tried
Import-Module MSOnline -Verbose
And I can see Cmdlets being loaded. I tried creating profile.ps1 file in C:\WINDOWS\system32\WindowsPowerShell\v1.0\ location.
Everything works fine if I execute the code locally. I tried running a regular test .ps1 file 'disk.ps1' instead of my code, and that works fine because it doesn't load any modules:
get-WmiObject win32_logicaldisk -Computername $env:computername
What's the workaround to get a script with module to run properly? stdout is always empty.
Node is registered as 64 bit so I tried changing the cmd to
C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe
I tried to copy profile.ps1 there, copied the Module there but still doesn't work via rundeck.

From your description, since you are able to get a valid output when running the script directly from the server, it may be possible that you error could be related to the “Second Hop” that you use to login into MS-Online. Currently, the Rundeck Python-Winrm plugin supports Basic, ntlm or CredSSP Authentication, and CredSSP authentication allows you to perform the second hop successfully.

Related

Python script running installer.exe without user interaction

I am "translating" powershell scripts in to python, and I am having problems when it comes to run a installer.exe because it has to be automatic and with 0 interaction from the user. They just have to run the script and it installs both program and Microsoft Visual C++ versions. I mean, run, install and close on the background.
On powershell, it was so simple like doing this:
Start-Process -Wait -FilePath "$pwd\VC\vcredist2013_x64.exe" -ArgumentList "/passive /norestart" -PassThru | Out-Null
On python, i tried this and more:
import subprocess
def startProgram():
SW_HIDE = 0
info = subprocess.STARTUPINFO()
info.dwFlags = subprocess.STARTF_USESHOWWINDOW
info.wShowWindow = SW_HIDE
VC13=subprocess.Popen(r'vcredist2013_x64.exe', startupinfo=info)
VC13.wait()
startProgram()
But they are not working, the wizard still pops up.
You can try using the subprocess.run method instead, with the stdout and stderr parameters redirected to subprocess.DEVNULL. The /passive and /norestart arguments can be passed using the args parameter.
Here's an updated code snippet:
import subprocess
def startProgram():
subprocess.run(["vcredist2013_x64.exe", "/passive", "/norestart"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
startProgram()

Command works on Command Prompt but it does not work when called with subprocess.run() or os.system() in python

Python 3.10.6
Windows 10
I have a python function that executes a DXL script using subsystem.run() or os.system() (whichever works best I guess). The problem is that when I run a custom command using python it does not work, but when I paste the same command in the command prompt, it works. I should also clarify that command prompt is not the ms store windows terminal (cannot run ibm doors commands there for some reason). It is the OG prompt
I need to use both python and IBM Doors for the solution.
Here is a summer version of my code (Obviously, the access values are not real):
#staticmethod
def run_dxl_importRTF():
dquotes = chr(0x22) # ASCII --> "
module_name = "TEST_TEMP"
script_path = "importRTF.dxl"
script_do_nothing_path = "doNothing.dxl"
user = "user"
password = "pass"
database_config = "11111#11.11.1111.0"
doors_path = dquotes + r"C:\Program Files\IBM\Rational\DOORS\9.7\bin\doors.exe" + dquotes
file_name = "LIBC_String.rtf"
# Based On:
# "C:\Program Files\IBM\Rational\DOORS\9.7\\bin\doors.exe" -dxl "string pModuleName = \"%~1\";string pFilename = \"%~2\";#include <importRTF.dxl>" -f "%TEMP%" -b "doNothing.dxl" -d 11111#11.11.1111.0 -user USER -password PASSWORD
script_arguments = f"{dquotes}string pModuleName=\{dquotes}{module_name}\{dquotes};string pFileName=\{dquotes}{file_name}\{dquotes};#include <{script_path}>{dquotes}"
command = [doors_path, "-dxl", script_arguments, "-f", "%TEMP%", "-b", script_do_nothing_path, '-d', database_config, '-user', user, '-password', password]
res = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
print(f"COMMAND:\n{' '.join(res.args)}")
print(f"STDERR: {repr(res.stderr)}")
print(f'STDOUT: {res.stdout}')
print(f'RETURN CODE: {res.returncode}')
return
PYTHON SCRIPT OUTPUT:
COMMAND:
"C:\Program Files\IBM\Rational\DOORS\9.7\bin\doors.exe" -dxl "string pModuleName=\"TEST_TEMP\";string pFileName=\"LIBC_String.rtf\";#include <importRTF.dxl>" -f %TEMP% -b doNothing.dxl -d 11111#11.11.1111.0 -user USER_TEMP -password PASS_TEMP
STDERR: 'The system cannot find the path specified.\n'
STDOUT:
RETURN CODE: 1
When I run the same command in the command prompt, it works (dxl script is compiled).
I identified the problem which is the script_argument variable. Meaning that, when I try to just enter the IBM Doors server without compiling a DXL script, it works on python and the command prompt.
The python script needs to be dynamic meaning that all of the initial declared variables can change value and have a path string in it. I am also trying to avoid .bat files. They also did not work with dynamic path values
Thanks for your time
I tried:
Changing CurrentDirectory (cwd) to IBM Doors
os.system()
Multiple workarounds
Tried IBM Doors path without double quotes (it doesnt work because of the whitespaces)
.bat files
When calling subprocess.run with a command list and shell=True, python will expand the command list to a string, adding more quoting along the way. The details are OS dependent (on Windows, you always have to expand the list to a command) but you can see the result via the subprocess.list2cmdline() function.
Your problem is these extra escapes. Instead of using a list, build a shell command string that already contains the escaping you want. You can also use ' for quoting strings so that internal " needed for shell quoting can be entered literally.
Putting it all together (and likely messing something up here), you would get
#staticmethod
def run_dxl_importRTF():
module_name = "TEST_TEMP"
script_path = "importRTF.dxl"
script_do_nothing_path = "doNothing.dxl"
user = "user"
password = "pass"
database_config = "11111#11.11.1111.0"
doors_path = r"C:\Program Files\IBM\Rational\DOORS\9.7\bin\doors.exe"
file_name = "LIBC_String.rtf"
script_arguments = (rf'string pModuleName=\"{module_name}\";'
'string pFileName=\"{file_name}\";'
'#include <{script_path}>')
command = (f'"{doors_path}" -dxl "{script_arguments}" -f "%TEMP%"'
' -b "{script_do_nothing_path}" -d {database_config}'
' -user {user} -password {pass}')
res = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
print(f"COMMAND:\n{' '.join(res.args)}")
print(f"STDERR: {repr(res.stderr)}")
print(f'STDOUT: {res.stdout}')
print(f'RETURN CODE: {res.returncode}')

Execute a program with python, then send API commands to that program

I'm trying to write a python script that can launch DaVinci Resolve in headless mode, then send it some commands via its API, then close it.
What I'm looking for would look something like
Open resolve.exe with argument --nogui
Do stuff with the API here
Terminate this instance of Resolve
I've managed to launch an instance of Resolve in headless. But it always ends up being a subprocess of something else. While it's running as a subprocess, I can't get the API to communicate with it.
Here's the code of tried
import subprocess
args = ["C:\Program Files\Blackmagic Design\DaVinci Resolve\Resolve.exe", '--nogui']
resolve_headles = subprocess.Popen(args)
from python_get_resolve import GetResolve
resolve = GetResolve()
This should return an object of Resolve, but it always fails.
I believe this is because its running as a subprocess of my IDE
I've also tried this
from subprocess import call
dir = "C:\Program Files\Blackmagic Design\DaVinci Resolve"
cmdline = "Resolve.exe --nogui"
rc = call("start cmd /K " + cmdline, cwd=dir, shell=True)
This just has the same problem of Resolve running as a subprocess of Windows Command Processor.

Python - Get current user folder when launching project remotely

I have a program i am launching from a Jenkins server.
I created an exe using pyinstaller and installed it on two computers. and then Jenkins calls them both.
At first i used os.path.expanduser('~') to get the user path but it returned
"C:\Windows\System32\config\systemprofile"
After i tried to just get the user name using os.environ['USERPROFILE']
still got:
"C:\Windows\System32\config\systemprofile"
Lastly i found a different solution that didnt require os module and tried:
import ctypes.wintypes
CSIDL_PERSONAL = 5 # My Documents
SHGFP_TYPE_CURRENT = 0 # Get current, not default value
buf= ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)
ctypes.windll.shell32.SHGetFolderPathW(None, CSIDL_PERSONAL, None,
SHGFP_TYPE_CURRENT, buf)
print(buf.value)
That gave no value back in my log value.
If i run the exe locally, all the value return back normally.
The batch command i run is
start /wait C:\JenkinsResources\MarinaMain.exe
So im drawing a blank as how i can get this program to find the user folder of the computer it is on, when i call it remotely.
So to get this to work I first downloaded the Windows Power Shell plugin.
After the restart I used the command:
$PCAndUserName = Get-WMIObject -class Win32_ComputerSystem | select username
$UserName = [string]$PCAndUserName
$UserName = $UserName.split("\\")[-1]
$UserName = $UserName -replace ".{1}$"
$UserName
That gave me the name of the current user on the computer regardless of if I launch remotely with Jenkins.

Script to capture everything on screen

So I have this python3 script that does a lot of automated testing for me, it takes roughly 20 minutes to run, and some user interaction is required. It also uses paramiko to ssh to a remote host for a separate test.
Eventually, I would like to hand this script over to the rest of my team however, it has one feature missing: evidence collection!
I need to capture everything that appears on the terminal to a file. I have been experimenting with the Linux command 'script'. However, I cannot find an automated method of starting script, and executing the script.
I have a command in /usr/bin/
script log_name;python3.5 /home/centos/scripts/test.py
When I run my command, it just stalls. Any help would be greatly appreciated!
Thanks :)
Is a redirection of the output to a file what you need ?
python3.5 /home/centos/scripts/test.py > output.log 2>&1
Or if you want to keep the output on the terminal AND save it into a file:
python3.5 /home/centos/scripts/test.py 2>&1 | tee output.log
I needed to do this, and ended up with a solution that combined pexpect and ttyrec.
ttyrec produces output files that can be played back with a few different player applications - I use TermTV and IPBT.
If memory serves, I had to use pexpect to launch ttyrec (as well as my test's other commands) because I was using Jenkins to schedule the execution of my test, and pexpect seemed to be the easiest way to get a working interactive shell in a Jenkins job.
In your situation you might be able to get away with using just ttyrec, and skip the pexpect step - try running ttyrec -e command as mentioned in the ttyrec docs.
Finally, on the topic of interactive shells, there's an alternative to pexpect named "empty" that I've had some success with too - see http://empty.sourceforge.net/. If you're running Ubuntu or Debian you can install empty with apt-get install empty-expect
I actually managed to do it in python3, took a lot of work, but here is the python solution:
def record_log(output):
try:
with open(LOG_RUN_OUTPUT, 'a') as file:
file.write(output)
except:
with open(LOG_RUN_OUTPUT, 'w') as file:
file.write(output)
def execute(cmd, store=True):
proc = Popen(cmd.encode("utf8"), shell=True, stdout=PIPE, stderr=PIPE)
output = "\n".join((out.decode()for out in proc.communicate()))
template = '''Command:\n====================\n%s\nResult:\n====================\n%s'''
output = template % (cmd, output)
print(output)
if store:
record_log(output)
return output
# SSH function
def ssh_connect(start_message, host_id, user_name, key, stage_commands):
print(start_message)
try:
ssh.connect(hostname=host_id, username=user_name, key_filename=key, timeout=120)
except:
print("Failed to connect to " + host_id)
for command in stage_commands:
try:
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(command)
except:
input("Paused, because " + command + " failed to run.\n Please verify and press enter to continue.")
else:
template = '''Command:\n====================\n%s\nResult:\n====================\n%s'''
output = ssh_stderr.read() + ssh_stdout.read()
output = template % (command, output)
record_log(output)
print(output)

Categories

Resources