Been working through this for a minute now, and figured I would ask here as I am sure it is something simple that I am missing.
Trying to silently run an installer using subprocess.run() (Have used subprocess.Popen, and os.system as well). I pass the full installer path, along with the arguments (As a list). Using all of the above, the installer runs successfully, and python waits for the installer to finish. I added a check to look at the PID of the installer process as just a backup check. However, running the script in full, I install some prerequisites (mainly dotnet 4.8) and then I restart. For cleanliness, I add the python script + arguments to the windows RunOnce registry key so it runs on next login. That works fine, but the installer never actually runs. It returns a 0 exit code, but never actually runs. Digging into it with event viewer, child processes (and maybe the parent process) is run by the NT System account. This causes the installer to silently fails since the System account doesn't have the permissions to run it. Looking for a way to run everything as a user to avoid the System account being used. Hence the question about running as an alternate user or the logged in user. (I don't know if this works on windows in all honesty)
Thoughts? Sample code snippets are below as well. Targeting Windows Server 2016+. Using python 3.11.2
Typical install code looks like the below. Installing it with subprocess works fine when run from command prompt manually. Its only when the RunOnce registry key triggers it does it silently fail. This leads me to believe its just the permissions/user error.
I also took a look at Python subprocess.Popen as different user on Windows but didn't have much luck implementing this. (Given it is 13 years old at this point)
# Installer path
installer = os.path.dirname(os.path.realpath(__file__)) + "\\setup.exe"
# Command for installing silently
# variables referenced here are all strings
ss_command = [
installer,
"-q",
"-s",
"InstallSecretServer=1",
"InstallPrivilegeManager=1",
'SecretServerUserDisplayName="Administrator"',
'SecretServerUserName="Administrator"',
"SecretServerUserPassword=" + '"' + administrator_password + '"',
"SecretServerAppUserName=" + '"' + service_account + '"',
"SecretServerAppPassword=" + '"' + service_account_password + '"',
"DatabaseIsUsingWindowsAuthentication=True",
"DatabaseServer=" + '"' + sql_hostname + '"',
"DatabaseName=" + '"' + database_name + '"',
"/l",
log_file
]
# Install attempt using Popen
installer_process = subprocess.Popen(ss_command)
# Install attempt using run
installer_process = subprocess.run(ss_command, capture_output=True, shell=False)
# Below are the functions for running executables and code from the registry.
# These work just fine, and arguments fed to the original script get passed to the registry key as well.
# Define a function to run an executable at boot
def run_at_startup_set(appname, arguments, path=None, user=False):
# Store the entry in the registry for running the application at startup
# Open the registry key path for applications that are run at login
key = win32api.RegOpenKeyEx(
win32con.HKEY_CURRENT_USER if user else win32con.HKEY_LOCAL_MACHINE,
Startup_Key_Path,
0,
win32con.KEY_WRITE | win32con.KEY_QUERY_VALUE
)
# Make sure the application is not already in the registry
i = 0
while True:
try:
name, _, _ = win32api.RegEnumValue(key, i)
except pywintypes.error as e:
if e.winerror == winerror.ERROR_NO_MORE_ITEMS:
break
else:
raise
if name == appname:
win32api.RegCloseKey(key)
return
i += 1
# Add arguments to the key
run_parameters = path or win32api.GetModuleFileName(0)
for arg in arguments:
run_parameters += ' "' + str(arg) + '"'
# Create a new key
win32api.RegSetValueEx(key, appname, 0, win32con.REG_SZ, run_parameters)
# Close the key
win32api.RegCloseKey(key)
return
# Define a function to run a script at boot
def run_script_at_startup_set(appname, arguments, user=False):
# Like run_at_startup_set(), but for source code files
run_at_startup_set(
appname,
arguments,
# Set the interpreter path (returned by GetModuleFileName())
# followed by the path of the current Python file (__file__).
'{} "{}"'.format(win32api.GetModuleFileName(0), __file__),
user
)
return