[python] How to run python script with elevated privilege on windows

I am writing a pyqt application which require to execute admin task. I would prefer to start my script with elevate privilege. I am aware that this question is asked many times in SO or in other forum. But the solution people are suggesting is to have a look at this SO question Request UAC elevation from within a Python script?

However, I am unable to execute the sample code given in the link. I have put this code on top of the main file and tried to execute it.

import os
import sys
import win32com.shell.shell as shell
ASADMIN = 'asadmin'

if sys.argv[-1] != ASADMIN:
    script = os.path.abspath(sys.argv[0])
    params = ' '.join([script] + sys.argv[1:] + [ASADMIN])
    shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params)
    sys.exit(0)
print "I am root now."

It actually ask permission to elevate but print line never get executed. Somebody can help me to run the above code successfully. Thanks in advance.

This question is related to python windows admin elevated-privileges

The answer is


Here is a solution which needed ctypes module only. Support pyinstaller wrapped program.

#!python
# coding: utf-8
import sys
import ctypes

def run_as_admin(argv=None, debug=False):
    shell32 = ctypes.windll.shell32
    if argv is None and shell32.IsUserAnAdmin():
        return True

    if argv is None:
        argv = sys.argv
    if hasattr(sys, '_MEIPASS'):
        # Support pyinstaller wrapped program.
        arguments = map(unicode, argv[1:])
    else:
        arguments = map(unicode, argv)
    argument_line = u' '.join(arguments)
    executable = unicode(sys.executable)
    if debug:
        print 'Command line: ', executable, argument_line
    ret = shell32.ShellExecuteW(None, u"runas", executable, argument_line, None, 1)
    if int(ret) <= 32:
        return False
    return None


if __name__ == '__main__':
    ret = run_as_admin()
    if ret is True:
        print 'I have admin privilege.'
        raw_input('Press ENTER to exit.')
    elif ret is None:
        print 'I am elevating to admin privilege.'
        raw_input('Press ENTER to exit.')
    else:
        print 'Error(ret=%d): cannot elevate privilege.' % (ret, )

in comments to the answer you took the code from someone says ShellExecuteEx doesn't post its STDOUT back to the originating shell. so you will not see "I am root now", even though the code is probably working fine.

instead of printing something, try writing to a file:

import os
import sys
import win32com.shell.shell as shell
ASADMIN = 'asadmin'

if sys.argv[-1] != ASADMIN:
    script = os.path.abspath(sys.argv[0])
    params = ' '.join([script] + sys.argv[1:] + [ASADMIN])
    shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params)
    sys.exit(0)
with open("somefilename.txt", "w") as out:
    print >> out, "i am root"

and then look in the file.


Make sure you have python in path,if not,win key + r, type in "%appdata%"(without the qotes) open local directory, then go to Programs directory ,open python and then select your python version directory. Click on file tab and select copy path and close file explorer.

Then do win key + r again, type control and hit enter. search for environment variables. click on the result, you will get a window. In the bottom right corner click on environmental variables. In the system side find path, select it and click on edit. In the new window, click on new and paste the path in there. Click ok and then apply in the first window. Restart your PC. Then do win + r for the last time, type cmd and do ctrl + shift + enter. Grant the previliges and open file explorer, goto your script and copy its path. Go back into cmd , type in "python" and paste the path and hit enter. Done


This worked for me:


import win32com.client as client

required_command = "cmd" # Enter your command here

required_password = "Simple1" # Enter your password here

def run_as(required_command, required_password):
    shell = client.Dispatch("WScript.shell")
    shell.Run(f"runas /user:administrator {required_command}")
    time.sleep(1)
    shell.SendKeys(f"{required_password}\r\n", 0)


if __name__ = '__main__':
    run_as(required_command, required_password)

Below are the references I used for above code: https://win32com.goermezer.de/microsoft/windows/controlling-applications-via-sendkeys.html https://www.oreilly.com/library/view/python-cookbook/0596001673/ch07s16.html


  1. make a batch file
  2. add python.exe "(your py file here)" with the quotation marks
  3. save the batch file
  4. right click, then click run as administrator

I wanted a more enhanced version so I ended up with a module which allows: UAC request if needed, printing and logging from nonprivileged instance (uses ipc and a network port) and some other candies. usage is just insert elevateme() in your script: in nonprivileged it listen for privileged print/logs and then exits returning false, in privileged instance it returns true immediately. Supports pyinstaller.

prototype:

# xlogger : a logger in the server/nonprivileged script
# tport : open port of communication, 0 for no comm [printf in nonprivileged window or silent]
# redir : redirect stdout and stderr from privileged instance
#errFile : redirect stderr to file from privileged instance
def elevateme(xlogger=None, tport=6000, redir=True, errFile=False):

winadmin.py

#!/usr/bin/env python
# -*- coding: utf-8; mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vim: fileencoding=utf-8 tabstop=4 expandtab shiftwidth=4

# (C) COPYRIGHT © Preston Landers 2010
# (C) COPYRIGHT © Matteo Azzali 2020
# Released under the same license as Python 2.6.5/3.7


import sys, os
from traceback import print_exc
from multiprocessing.connection import Listener, Client
import win32event #win32com.shell.shell, win32process
import builtins as __builtin__ # python3

# debug suffixes for remote printing
dbz=["","","",""] #["J:","K:", "G:", "D:"]
LOGTAG="LOGME:"

wrconn = None


#fake logger for message sending
class fakelogger:
    def __init__(self, xlogger=None):
        self.lg = xlogger
    def write(self, a):
        global wrconn
        if wrconn is not None:
            wrconn.send(LOGTAG+a)
        elif self.lg is not None:
            self.lg.write(a)
        else:
            print(LOGTAG+a)
        

class Writer():
    wzconn=None
    counter = 0
    def __init__(self, tport=6000,authkey=b'secret password'):
        global wrconn
        if wrconn is None:
            address = ('localhost', tport)
            try:
                wrconn = Client(address, authkey=authkey)
            except:
                wrconn = None
            wzconn = wrconn
            self.wrconn = wrconn
        self.__class__.counter+=1
        
    def __del__(self):
        self.__class__.counter-=1
        if self.__class__.counter == 0 and wrconn is not None:
            import time
            time.sleep(0.1) # slows deletion but is enough to print stderr
            wrconn.send('close')
            wrconn.close()
    
    def sendx(cls, mesg):
        cls.wzconn.send(msg)
        
    def sendw(self, mesg):
        self.wrconn.send(msg)
        

#fake file to be passed as stdout and stderr
class connFile():
    def __init__(self, thekind="out", tport=6000):
        self.cnt = 0
        self.old=""
        self.vg=Writer(tport)
        if thekind == "out":
            self.kind=sys.__stdout__
        else:
            self.kind=sys.__stderr__
        
    def write(self, *args, **kwargs):
        global wrconn
        global dbz
        from io import StringIO # # Python2 use: from cStringIO import StringIO
        mystdout = StringIO()
        self.cnt+=1
        __builtin__.print(*args, **kwargs, file=mystdout, end = '')
        
        #handles "\n" wherever it is, however usually is or string or \n
        if "\n" not in mystdout.getvalue():
            if mystdout.getvalue() != "\n":
                #__builtin__.print("A:",mystdout.getvalue(), file=self.kind, end='')
                self.old += mystdout.getvalue()
            else:
                #__builtin__.print("B:",mystdout.getvalue(), file=self.kind, end='')
                if wrconn is not None:
                    wrconn.send(dbz[1]+self.old)
                else:
                    __builtin__.print(dbz[2]+self.old+ mystdout.getvalue(), file=self.kind, end='')
                    self.kind.flush()
                self.old=""
        else:
                vv = mystdout.getvalue().split("\n")
                #__builtin__.print("V:",vv, file=self.kind, end='')
                for el in vv[:-1]:
                    if wrconn is not None:
                        wrconn.send(dbz[0]+self.old+el)
                        self.old = ""
                    else:
                        __builtin__.print(dbz[3]+self.old+ el+"\n", file=self.kind, end='')
                        self.kind.flush()
                        self.old=""
                self.old=vv[-1]

    def open(self):
        pass
    def close(self):
        pass
    def flush(self):
        pass
        
        
def isUserAdmin():
    if os.name == 'nt':
        import ctypes
        # WARNING: requires Windows XP SP2 or higher!
        try:
            return ctypes.windll.shell32.IsUserAnAdmin()
        except:
            traceback.print_exc()
            print ("Admin check failed, assuming not an admin.")
            return False
    elif os.name == 'posix':
        # Check for root on Posix
        return os.getuid() == 0
    else:
        print("Unsupported operating system for this module: %s" % (os.name,))
        exit()
        #raise (RuntimeError, "Unsupported operating system for this module: %s" % (os.name,))

def runAsAdmin(cmdLine=None, wait=True, hidden=False):

    if os.name != 'nt':
        raise (RuntimeError, "This function is only implemented on Windows.")

    import win32api, win32con, win32process
    from win32com.shell.shell import ShellExecuteEx

    python_exe = sys.executable
    arb=""
    if cmdLine is None:
        cmdLine = [python_exe] + sys.argv
    elif not isinstance(cmdLine, (tuple, list)):
        if isinstance(cmdLine, (str)):
            arb=cmdLine
            cmdLine = [python_exe] + sys.argv
            print("original user", arb)
        else:
            raise( ValueError, "cmdLine is not a sequence.")
    cmd = '"%s"' % (cmdLine[0],)

    params = " ".join(['"%s"' % (x,) for x in cmdLine[1:]])
    if len(arb) > 0:
        params += " "+arb
    cmdDir = ''
    if hidden:
        showCmd = win32con.SW_HIDE
    else:
        showCmd = win32con.SW_SHOWNORMAL
    lpVerb = 'runas'  # causes UAC elevation prompt.

    # print "Running", cmd, params

    # ShellExecute() doesn't seem to allow us to fetch the PID or handle
    # of the process, so we can't get anything useful from it. Therefore
    # the more complex ShellExecuteEx() must be used.

    # procHandle = win32api.ShellExecute(0, lpVerb, cmd, params, cmdDir, showCmd)

    procInfo = ShellExecuteEx(nShow=showCmd,
                              fMask=64,
                              lpVerb=lpVerb,
                              lpFile=cmd,
                              lpParameters=params)

    if wait:
        procHandle = procInfo['hProcess']    
        obj = win32event.WaitForSingleObject(procHandle, win32event.INFINITE)
        rc = win32process.GetExitCodeProcess(procHandle)
        #print "Process handle %s returned code %s" % (procHandle, rc)
    else:
        rc = procInfo['hProcess']

    return rc


# xlogger : a logger in the server/nonprivileged script
# tport : open port of communication, 0 for no comm [printf in nonprivileged window or silent]
# redir : redirect stdout and stderr from privileged instance
#errFile : redirect stderr to file from privileged instance
def elevateme(xlogger=None, tport=6000, redir=True, errFile=False):
    global dbz
    if not isUserAdmin():
        print ("You're not an admin.", os.getpid(), "params: ", sys.argv)

        import getpass
        uname = getpass.getuser()
        
        if (tport> 0):
            address = ('localhost', tport)     # family is deduced to be 'AF_INET'
            listener = Listener(address, authkey=b'secret password')
        rc = runAsAdmin(uname, wait=False, hidden=True)
        if (tport> 0):
            hr = win32event.WaitForSingleObject(rc, 40)
            conn = listener.accept()
            print ('connection accepted from', listener.last_accepted)
            sys.stdout.flush()
            while True:
                msg = conn.recv()
                # do something with msg
                if msg == 'close':
                    conn.close()
                    break
                else:
                    if msg.startswith(dbz[0]+LOGTAG):
                        if xlogger != None:
                            xlogger.write(msg[len(LOGTAG):])
                        else:
                            print("Missing a logger")
                    else:
                        print(msg)
                    sys.stdout.flush()
            listener.close()
        else: #no port connection, its silent
            WaitForSingleObject(rc, INFINITE);
        return False
    else:
        #redirect prints stdout on  master, errors in error.txt
        print("HIADM")
        sys.stdout.flush()
        if (tport > 0) and (redir):
            vox= connFile(tport=tport)
            sys.stdout=vox
            if not errFile:
                sys.stderr=vox
            else:
                vfrs=open("errFile.txt","w")
                sys.stderr=vfrs
            
            #print("HI ADMIN")
        return True


def test():
    rc = 0
    if not isUserAdmin():
        print ("You're not an admin.", os.getpid(), "params: ", sys.argv)
        sys.stdout.flush()
        #rc = runAsAdmin(["c:\\Windows\\notepad.exe"])
        rc = runAsAdmin()
    else:
        print ("You are an admin!", os.getpid(), "params: ", sys.argv)
        rc = 0
    x = raw_input('Press Enter to exit.')
    return rc
    
if __name__ == "__main__":
    sys.exit(test())

Here is a solution with an stdout redirection:

def elevate():
    import ctypes, win32com.shell.shell, win32event, win32process
    outpath = r'%s\%s.out' % (os.environ["TEMP"], os.path.basename(__file__))
    if ctypes.windll.shell32.IsUserAnAdmin():
        if os.path.isfile(outpath):
            sys.stderr = sys.stdout = open(outpath, 'w', 0)
        return
    with open(outpath, 'w+', 0) as outfile:
        hProc = win32com.shell.shell.ShellExecuteEx(lpFile=sys.executable, \
            lpVerb='runas', lpParameters=' '.join(sys.argv), fMask=64, nShow=0)['hProcess']
        while True:
            hr = win32event.WaitForSingleObject(hProc, 40)
            while True:
                line = outfile.readline()
                if not line: break
                sys.stdout.write(line)
            if hr != 0x102: break
    os.remove(outpath)
    sys.stderr = ''
    sys.exit(win32process.GetExitCodeProcess(hProc))

if __name__ == '__main__':
    elevate()
    main()

I can confirm that the solution by delphifirst works and is the easiest, simplest solution to the problem of running a python script with elevated privileges.

I created a shortcut to the python executable (python.exe) and then modified the shortcut by adding my script's name after the call to python.exe. Next I checked "run as administrator" on the "compatibility tab" of the shortcut. When the shortcut is executed, you get a prompt asking permission to run the script as an administrator.

My particular python application was an installer program. The program allows installing and uninstalling another python app. In my case I created two shortcuts, one named "appname install" and the other named "appname uninstall". The only difference between the two shortcuts is the argument following the python script name. In the installer version the argument is "install". In the uninstall version the argument is "uninstall". Code in the installer script evaluates the argument supplied and calls the appropriate function (install or uninstall) as needed.

I hope my explanation helps others more quickly figure out how to run a python script with elevated privileges.


I found a very easy solution to this problem.

  1. Create a shortcut for python.exe
  2. Change the shortcut target into something like C:\xxx\...\python.exe your_script.py
  3. Click "advance..." in the property panel of the shortcut, and click the option "run as administrator"

I'm not sure whether the spells of these options are right, since I'm using Chinese version of Windows.


Also if your working directory is different than you can use lpDirectory

    procInfo = ShellExecuteEx(nShow=showCmd,
                          lpVerb=lpVerb,
                          lpFile=cmd,
                          lpDirectory= unicode(direc),
                          lpParameters=params)

Will come handy if changing the path is not a desirable option remove unicode for python 3.X


It worth mentioning that if you intend to package your application with PyInstaller and wise to avoid supporting that feature by yourself, you can pass the --uac-admin or --uac-uiaccess argument in order to request UAC elevation on start.


Examples related to python

programming a servo thru a barometer Is there a way to view two blocks of code from the same file simultaneously in Sublime Text? python variable NameError Why my regexp for hyphenated words doesn't work? Comparing a variable with a string python not working when redirecting from bash script is it possible to add colors to python output? Get Public URL for File - Google Cloud Storage - App Engine (Python) Real time face detection OpenCV, Python xlrd.biffh.XLRDError: Excel xlsx file; not supported Could not load dynamic library 'cudart64_101.dll' on tensorflow CPU-only installation

Examples related to windows

"Permission Denied" trying to run Python on Windows 10 A fatal error occurred while creating a TLS client credential. The internal error state is 10013 How to install OpenJDK 11 on Windows? I can't install pyaudio on Windows? How to solve "error: Microsoft Visual C++ 14.0 is required."? git clone: Authentication failed for <URL> How to avoid the "Windows Defender SmartScreen prevented an unrecognized app from starting warning" XCOPY: Overwrite all without prompt in BATCH Laravel 5 show ErrorException file_put_contents failed to open stream: No such file or directory how to open Jupyter notebook in chrome on windows Tensorflow import error: No module named 'tensorflow'

Examples related to admin

MongoDB - admin user not authorized How to run python script with elevated privilege on windows django admin - add custom form fields that are not part of the model Batch script: how to check for admin rights

Examples related to elevated-privileges

How to run python script with elevated privilege on windows How to run vbs as administrator from vbs? How can I auto-elevate my batch file, so that it requests from UAC administrator rights if required? How do I force my .NET application to run as administrator? How to request Administrator access inside a batch file