How can I auto-elevate my batch file, so that it requests from UAC administrator rights if required?
Asked Answered
P

19

268

I want my batch file to only run elevated. If not elevated, provide an option for the user to relaunch batch as elevated.

I'm writing a batch file to set a system variable, copy two files to a Program Files location, and start a driver installer. If a Windows 7/Windows Vista user (UAC enabled and even if they are a local admin) runs it without right-clicking and selecting "Run as Administrator", they will get 'Access Denied' copying the two files and writing the system variable.

I would like to use a command to automatically restart the batch as elevated if the user is in fact an administrator. Otherwise, if they are not an administrator, I want to tell them that they need administrator privileges to run the batch file. I'm using xcopy to copy the files and REG ADD to write the system variable. I'm using those commands to deal with possible Windows XP machines. I've found similar questions on this topic, but nothing that deals with relaunching a batch file as elevated.

Piscatorial answered 12/8, 2011 at 18:55 Comment(8)
Check out what I've posted - you don't need any external tool, the script automatically checks for admin rights and auto-elevates itself if required.Feathered
Please consider if Matt's answer would be the ticked one? Seems so to me.Rhetor
possible duplicate of How to request Administrator access inside a batch fileChurchwoman
Please regard the new Windows 10 hints in the comments section of the batch script I have posted.Feathered
From cmd: @powershell Start-Process cmd -Verb runas. From Powershell just drop @powershell. This starts cmd with elevated rights.Trimester
Or try pure windows way using schtasks: stackoverflow.com/questions/19098101/…Soupspoon
The question is not very clear because the term auto-elevate refers to elevating without prompt. In case you were searching for that, see the exploit here.Posology
@Rhetor - I'm honored to hear that, however, the user PDixon724 who asked this question was last seen 10 years ago. He's the only one who can change the accepted answer.Feathered
D
25

You can have the script call itself with psexec's -h option to run elevated.

I'm not sure how you would detect if it's already running as elevated or not... maybe re-try with elevated perms only if there's an Access Denied error?

Or, you could simply have the commands for the xcopy and reg.exe always be run with psexec -h, but it would be annoying for the end-user if they need to input their password each time (or insecure if you included the password in the script)...

Duchy answered 12/8, 2011 at 19:4 Comment(6)
Thanks for the response. Unfortunately, I don't think I can use anything outside of stock Windows Vista/7 tools because this will be going out to customers outside of my office. I don't think I can legally distribute PSExec.Piscatorial
Yup, I think you are right about that--even though PSExec is now a Microsoft tool (since they bought out the Sysinternals guys!) the EULA does forbid distribution :(Duchy
I think my options are pretty limited. If I knew how to code in VB, I could make it an exe with an admin manifest, but I wouldn't even know where to start. I guess I'll just warn at the beginning of the batch to run as admin if they're running Windows Vista/7. Thanks all.Piscatorial
Another 3rd-party tool that might be freely redistributable and easy to integrate and learn is AutoIt; this page demonstrates how the script requests elevated privileges.Duchy
Thanks ewall. It looks like I can redistribute AutoIt. I'll go down that road. Thanks again for the help!Piscatorial
psexec -h doesn't work: 'Couldn't install PSEXESVC service: Access is denied.'. You need to already have the administrator rights to run psexec.Nureyev
F
369

There is an easy way without the need to use an external tool - it runs fine with Windows 7, 8, 8.1, 10 and 11 and is backwards-compatible too (Windows XP doesn't have any UAC, thus elevation is not needed - in that case the script just proceeds).

Check out this code (I was inspired by the code by NIronwolf posted in the thread Batch File - "Access Denied" On Windows 7? 1), but I've improved it - in my version there isn't any directory created and removed to check for administrator privileges):

::::::::::::::::::::::::::::::::::::::::::::
:: Elevate.cmd - Version 4
:: Automatically check & get admin rights
:: see "https://mcmap.net/q/13503/-how-can-i-auto-elevate-my-batch-file-so-that-it-requests-from-uac-administrator-rights-if-required" for description
::::::::::::::::::::::::::::::::::::::::::::
 @echo off
 CLS
 ECHO.
 ECHO =============================
 ECHO Running Admin shell
 ECHO =============================

:init
 setlocal DisableDelayedExpansion
 set cmdInvoke=1
 set winSysFolder=System32
 set "batchPath=%~dpnx0"
 rem this works also from cmd shell, other than %~0
 for %%k in (%0) do set batchName=%%~nk
 set "vbsGetPrivileges=%temp%\OEgetPriv_%batchName%.vbs"
 setlocal EnableDelayedExpansion

:checkPrivileges
  NET FILE 1>NUL 2>NUL
  if '%errorlevel%' == '0' ( goto gotPrivileges ) else ( goto getPrivileges )

:getPrivileges
  if '%1'=='ELEV' (echo ELEV & shift /1 & goto gotPrivileges)
  ECHO.
  ECHO **************************************
  ECHO Invoking UAC for Privilege Escalation
  ECHO **************************************

  ECHO Set UAC = CreateObject^("Shell.Application"^) > "%vbsGetPrivileges%"
  ECHO args = "ELEV " >> "%vbsGetPrivileges%"
  ECHO For Each strArg in WScript.Arguments >> "%vbsGetPrivileges%"
  ECHO args = args ^& strArg ^& " "  >> "%vbsGetPrivileges%"
  ECHO Next >> "%vbsGetPrivileges%"
  
  if '%cmdInvoke%'=='1' goto InvokeCmd 

  ECHO UAC.ShellExecute "!batchPath!", args, "", "runas", 1 >> "%vbsGetPrivileges%"
  goto ExecElevation

:InvokeCmd
  ECHO args = "/c """ + "!batchPath!" + """ " + args >> "%vbsGetPrivileges%"
  ECHO UAC.ShellExecute "%SystemRoot%\%winSysFolder%\cmd.exe", args, "", "runas", 1 >> "%vbsGetPrivileges%"

:ExecElevation
 "%SystemRoot%\%winSysFolder%\WScript.exe" "%vbsGetPrivileges%" %*
 exit /B

:gotPrivileges
 setlocal & cd /d %~dp0
 if '%1'=='ELEV' (del "%vbsGetPrivileges%" 1>nul 2>nul  &  shift /1)

 ::::::::::::::::::::::::::::
 ::START
 ::::::::::::::::::::::::::::
 REM Run shell as admin (example) - put here code as you like
 ECHO %batchName% Arguments: P1=%1 P2=%2 P3=%3 P4=%4 P5=%5 P6=%6 P7=%7 P8=%8 P9=%9
 cmd /k

The script takes advantage of the fact that NET FILE requires administrator privilege and returns errorlevel 1 if you don't have it. The elevation is achieved by creating a script which re-launches the batch file to obtain privileges. This causes Windows to present the UAC dialog and asks you for the administrator account and password.

I have tested it with Windows 7, 8, 8.1, 10, 11 and with Windows XP - it works fine for all. The advantage is, after the start point you can place anything that requires system administrator privileges, for example, if you intend to re-install and re-run a Windows service for debugging purposes (assumed that mypackage.msi is a service installer package):

msiexec /passive /x mypackage.msi
msiexec /passive /i mypackage.msi
net start myservice

Without this privilege elevating script, UAC would ask you three times for your administrator user and password - now you're asked only once at the beginning, and only if required.


If your script just needs to show an error message and exit if there aren't any administrator privileges instead of auto-elevating, this is even simpler: You can achieve this by adding the following at the beginning of your script:

@ECHO OFF & CLS & ECHO.
NET FILE 1>NUL 2>NUL & IF ERRORLEVEL 1 (ECHO You must right-click and select &
  ECHO "RUN AS ADMINISTRATOR"  to run this batch. Exiting... & ECHO. &
  PAUSE & EXIT /D)
REM ... proceed here with admin rights ...

This way, the user has to right-click and select "Run as administrator". The script will proceed after the REM statement if it detects administrator rights, otherwise exit with an error. If you don't require the PAUSE, just remove it. Important: NET FILE [...] EXIT /D) must be on the same line. It is displayed here in multiple lines for better readability!


On some machines, I've encountered issues, which are solved in the new version above already. One was due to different double quote handling, and the other issue was due to the fact that UAC was disabled (set to lowest level) on a Windows 7 machine, hence the script calls itself again and again.

I have fixed this now by stripping the quotes in the path and re-adding them later, and I've added an extra parameter which is added when the script re-launches with elevated rights.

The double quotes are removed by the following (details are here):

setlocal DisableDelayedExpansion
set "batchPath=%~0"
setlocal EnableDelayedExpansion

You can then access the path by using !batchPath!. It doesn't contain any double quotes, so it is safe to say "!batchPath!" later in the script.

The line

if '%1'=='ELEV' (shift & goto gotPrivileges)

checks if the script has already been called by the VBScript script to elevate rights, hence avoiding endless recursions. It removes the parameter using shift.


Update:

  • To avoid having to register the .vbs extension in Windows 10, I have replaced the line
    "%temp%\OEgetPrivileges.vbs"
    by
    "%SystemRoot%\System32\WScript.exe" "%temp%\OEgetPrivileges.vbs"
    in the script above; also added cd /d %~dp0 as suggested by Stephen (separate answer) and by Tomáš Zato (comment) to set script directory as default.

  • Now the script honors command line parameters being passed to it. Thanks to jxmallet, TanisDLJ and Peter Mortensen for observations and inspirations.

  • According to Artjom B.'s hint, I analyzed it and have replaced SHIFT by SHIFT /1, which preserves the file name for the %0 parameter

  • Added del "%temp%\OEgetPrivileges_%batchName%.vbs" to the :gotPrivileges section to clean up (as mlt suggested). Added %batchName% to avoid impact if you run different batches in parallel. Note that you need to use for to be able to take advantage of the advanced string functions, such as %%~nk, which extracts just the filename.

  • Optimized script structure, improvements (added variable vbsGetPrivileges which is now referenced everywhere allowing to change the path or name of the file easily, only delete .vbs file if batch needed to be elevated)

  • In some cases, a different calling syntax was required for elevation. If the script does not work, check the following parameters:
    set cmdInvoke=0
    set winSysFolder=System32
    Either change the 1st parameter to set cmdInvoke=1 and check if that already fixes the issue. It will add cmd.exe to the script performing the elevation.
    Or try to change the 2nd parameter to winSysFolder=Sysnative, this might help (but is in most cases not required) on 64 bit systems. (ADBailey has reported this). "Sysnative" is only required for launching 64-bit applications from a 32-bit script host (e.g. a Visual Studio build process, or script invocation from another 32-bit application).

  • To make it more clear how the parameters are interpreted, I am displaying it now like P1=value1 P2=value2 ... P9=value9. This is especially useful if you need to enclose parameters like paths in double quotes, e.g. "C:\Program Files".

  • If you want to debug the VBS script, you can add the //X parameter to WScript.exe as first parameter, as suggested here (it is described for CScript.exe, but works for WScript.exe too).

  • Bugfix provided by MiguelAngelo: batchPath is now returned correctly on cmd shell. This little script test.cmd shows the difference, for those interested in the details (run it in cmd.exe, then run it via double click from Windows Explorer):

    @echo off
    setlocal
    set a="%~0"
    set b="%~dpnx0"
    if %a% EQU %b% echo running shell execute
    if not %a% EQU %b% echo running cmd shell
    echo a=%a%, b=%b% 
    pause
    

Useful links:


1) Note that this link no longer exists, it is now showing a 404 error.

Feathered answered 4/9, 2012 at 13:31 Comment(68)
Great answer, although it amazes me slightly that you have to do all that to do something that is clearly necessary in some cases.Frenchpolish
Indeed, a command such as ELEVATE is clearly missing in the Windows batch language.Feathered
Perhaps it's easier with powershell which seems to be the approved scripting lanaguge for more complex things, but I never bothered to learn it so far :(Frenchpolish
The syntax in powershell is completely different (verb-noun syntax), but it allows you to call .NET assemblies easily. But it is a bit more difficult to handle it (signing scripts etc).Feathered
I haven't tested it with Windows Server 2012 explicitly, but it works with Windows 7 and 8 so I tend to say yes. However, to be sure you should test it before you use it. But on a Server, you are usually running scripts without user interaction and hence you would schedule to run it with proper user rights and would not need/want UAC confirmation because it would stop the execution of your scheduled task until an admin types in the user credentials.Feathered
My powershell method: https://mcmap.net/q/13503/-how-can-i-auto-elevate-my-batch-file-so-that-it-requests-from-uac-administrator-rights-if-required Not as nice as an "ELEVATE" command, but it doesn't require a temporary *.vbs.Teter
This counts for all proposed solutions here: doesn't work when the script has spaces in its name and you call it from the command promptRentschler
@Nicolas: I don't question why you would like to have spaces in the name - try using underscore (_) instead of spaces or use double quotes (") around path+name of the batch file.Feathered
I have already renamed the (legacy) bat file, but thanks for the info anywayRentschler
You might want to add cd %~dp0 before your own code. Otherwise, you'll find out that your working directory is something like C:\Windows\System32. Imagine your script accidentally changes something there...Pyromagnetic
@Tomáš Zato: Yes, that is correct. Note that Stephen has already suggested it. Take a look at the code snippet he has provided.Feathered
This does not seem to work with Windows 10. It did its job well inside 7 and 8/8.1, but not 10. It seems to hang somewhere after line 9.Arturo
@VitaminYes: I have verified it. It works with WIndows 10 as well, but you probably didn't notice that Windows dialog which pops up for the first time saying "How to open this file?" - select "Microsoft Windows Based Script Host", tickmark "Always use this App to open .vbs files" and click OK. Next time (and every subsequent time) the batch works just fine as it did in Win7/8.Feathered
@VitaminYes: The dialog mentioned before does the same as if you would type ftype VBSFile="%SystemRoot%\System32\WScript.exe" "%1" %* and then assoc .vbs=VBSFile into an admin console window. It registers the .vbs extension to be run with Windows Script Host - apparently Windows 10 doesn't have this association any longer per default so you have to do it once.Feathered
Another way for Windows 10 users - if you don't want to change the default registration of .vbs extensions as mentioned above - then you can replace the line "%temp%\OEgetPrivileges.vbs" in the script by "%SystemRoot%\System32\WScript.exe" "%temp%\OEgetPrivileges.vbs", which also worked fine for me.Feathered
The .vbs extension has its correct association. Wow, this is strange. I hit the return key after the ninth line prints and it ran fine. I wonder what I said "yes" to? Maybe it didn't have WSH after allArturo
@VitaminYes: Have you tried the updated version of the script I provided yesterday? It should run regardless of the .vbs extension registration.Feathered
Hi! I'm getting the dialog "This file does not have a program associated with it for performing this action" coming from line where OEgetPrivileges.vbs is executed. I haven't modified the script. The vbs is created and it is executed via WScript. Any ideas? Using Windows 7.Macintosh
@NeplatnyUdaj: I have just tested the updated script with Windows 7/64 bit, and on my PC it runs just fine. Check if %SystemRoot%\System32\WScript.exe is on your machine installed, then check my hints I gave earlier to VitaminYes (regarding registration via ftype and assoc). Maybe you have to install/activate Windows Scripting Host, which would be unusual because it comes out of the box in Windows 7 normally.Feathered
I've found out that the message is not from running the vb script, but from running the batch file via UAC from within the vbs script. It comes from this line: UAC.ShellExecute "C:\Users\Me\Documents\script.bat", "ELEV", "", "runas", 1. Also I have some strange Windows apparently, because NET FILE does not give an exception(yes, I've checked I'm not admin. I had to modify the check to use mkdir in forbidden directory instead.Macintosh
The script needs to be saved as .cmd, e.g. elevate.cmd. It can also be that your Windows installation have issues and needs a refresh (new setup) - I have tested the batch on multiple PCs and found no errors as you described. Try it on a colleague's PC to find that out. I cannot give support on your Windows installation.Feathered
Matt: The problem was with the group policy(or whatever, I'm not a windows expert) on the company machine. Running it on clean windows works.Macintosh
Thank you for letting me know. Yes, it is possible that some functions are blocked via policies on a company PC. A clean PC or VM installation is always a good one idea for comparison. @MacintoshFeathered
@Feathered Can you check whether there is some truth behind this rejected edit on your answer?Chiasma
@Artjom B.: Yes, there was an issue, but a bit different. Not removing the SHIFT is the solution, but appending a /1 parameter to the SHIFT command (see updated listing). It should work fine now. Thanks for bringing this to my attention!Feathered
Is there a way to cleanup %TEMP% and delete OEgetPrivileges.vbs upon termination?Imbrue
I thought about that too, there are several issues: One is that the file is locked until the batch ends, another is that if you start any second admin batch it will interfere unless you create a unique temp name. Yes it is possible but complicates things. @ImbrueFeathered
Since we already have admin privileges at some point, perhaps, it might worth considering adding a RunOnce registry entry into HKCU hive to delete the script on next logon.Imbrue
@mlt: I've added a cleanup to the script, it was easier than expected. Thank you for the hint!Feathered
is there a way to not have a prompt window to ask for UAC continue or cancel? Plan to run this in uDeploy or shell which needs to run silently without any prompt and interaction requirement.Organogenesis
@Princa: Yes - this "prompt" is coming from Windows directly. In the Windows control panel, type "UAC" in the search field and press enter. Then open "Change User Account Control settings" by double clicking on it and confirming with an admin account. A dialog opens up where you can drag a slider to "Never notify". Confirm with "OK", reboot your PC and the dialog should go away if you're logged in with a privileged (admin) account. But note this is not a recommended setting because it lowers security.Feathered
Hey, thanks for the scripts, it works very good if I run a bat file(let's say A.bat) with the codes. But when I try to include A.bat in B.bat and run B.bat, then the UAC will prompt but still not get administration right in this case. I am using "call A.bat" to include the scripts.Columbine
@Troy: Admin rights exist only within the context of the elevation script. If I understand it right, you need the admin rights in B.bat, but want to use A.bat just to elevate B.bat. If that is the case, call B.bat after the ::Start section of A.bat (i.e. do it the opposite way).Feathered
@Matt: Thanks for the comments. Yes, I do need the admin right in B.bat and I want user to double click to run B.bat at first. In B.bat, it includes the A.bat which has your codes fully. Here are my codes in B.bat: <code>call A.bat :: then call my custom codes</code> So, just like A.bat is a library and I want B.bat looks simple. But this won't work for me.Columbine
In this case, the only way I see is to include the elevation script in B.bat, and the code you have written needs to be appended. Or you can place the elevation in B.bat and call the library and your code from there with two calls, i.e. call A.bat and C.bat (just the simple code) from B.bat.Feathered
@Matt, Thanks Matt! Maybe it is just like you said, it exits only within context of elevation script. I can figure out other workaround for this. Thanks again.Columbine
If a parameter contains spaces it gets spitted into variables, so f.e. "te st" will be only accessible by %1 and %2. Any ideas on how to solve this?Guglielma
nevermind, I used https://mcmap.net/q/13751/-how-to-request-administrator-access-inside-a-batch-file which is working with spaces in parameters.Guglielma
@brauchle_andi: According to my tests, double quotes are passed through, i.e. if you specify Elevate.cmd "1st param" "2nd param" "3rd param" they are passed this way during elevation as %1 ... %3. If you omit the double quotes, then they are interpreted as 1st param 2nd param 3rd param, which is %1 ... %6 instead.Feathered
Thanks for this! I had some difficulty using it to launch a 64-bit application from a 32-bit host (part of a build script; Windows was redirecting to "Program Files" to "Program Files (x86)"). To solve, I replaced %SystemRoot%\System32\WScript.exe with %SystemRoot%\Sysnative\WScript.exe to force the use of 64-bit WScript.Propagandism
@ADBailey: You're welcome. However I was wondering about "Sysnative" - on my 64 bit Windows there is only C:\Windows\System32 and C:\Windows\SysWow64, but not C:\Windows\Sysnative. In both paths you can find Wscript.exe.Feathered
Ah, SysNative isn't a "real" directory, just a feature of the File System Redirector that Windows uses for 32-bit applications on 64-bit Windows. In such cases, requests to c:\Windows\System32 are redirected to c:\Windows\SysWow64, whilst requesting c:\Windows\Sysnative is a way of saying "yes, I'm a 32-bit application that is aware of the 64-bit OS and I really do want the proper System32". You can try this out by running c:\Windows\SysWow64\cmd.exe (32-bit) and listing the directories.Propagandism
P.S. a direct swap to Sysnative is only any good if your (Elevate.cmd) script is always being run in a 32-bit host: in a 64-bit host, Sysnative won't be found. If you want it to be flexible to either (as I ended up doing), you need to add a quick "if exists" condition to switch between Sysnative and System32. It's a niche problem and probably too fussy to edit your provided answer with, but I figured the information would be useful if anybody else ran into it.Propagandism
@ADBailey: Thanks for the explanation, very useful! I think the most practical way is to save a 64bit version of the elevate script, like Elevate64.cmd for 64 bit, and Elevate.cmd for 32 bit. The reason is that such a script would have more dependencies to 64 bit code and many IF's would complicate things ...Feathered
@ADBailey: I have provided a new version of the script, which includes a parameter for System32 ... this should make life easier for you. Also, I noticed that on some system a 2nd change was required, see the Updates section in the answer.Feathered
Great work. I personally ended up diverging from your script a bit in the end to suit my specific needs and do, in my case, genuinely need a script that can elevate a new 64-bit application when run from either a 32-bit or 64-bit host, but I'm sure that the extra flexibility will help others out. No obligation, but your answer would provide additional clarity if it stated explicitly that "Sysnative" is only for launching 64-bit applications from a 32-bit script host (e.g. a Visual Studio build process, or script invocation from another 32-bit application).Propagandism
@ADBailey: Thanks, I have added your explanation to the answer.Feathered
The Elevate.cmd script ends with cmd /k. Does this create a new shell process and stay there?Helga
@lit: Yes, that is the purpose - to show the admin console after elevating (for demonstration). If you don't need an admin console (because you just want to run your script elevated) remove cmd /k.Feathered
What's the purpose of pushd . this looks not very useful without a corresponding popd. It also has the cd /D on board, as I understand the explanation here: Pushd - change directory/folder | Windows CMD | SS64.com ...and my personal observationsJareb
@Jareb - cd /d %~dp0 sets the current directory to the script directory. The /d parameter is required to ensure that the drive is also changed if needed, not only the path. The command pushd . can possibly be removed, it was there from the beginning of development. If you (or someone who already removed and tested it) can confirm, I can remove it.Feathered
Yes, I already know the /d option for cd, but since I'm using a combination of pushd (which implicitly does cd /d -- that's in fact the purpose of the parameter) and popd, I don't need the extra cd /d. In your case here, I'd suggest to just remove the pointless & pushd .Jareb
@Jareb - Thanks for the hint, I've optimized the script and removed the pushd.Feathered
You're welcome, but... How exactly did you "optimize" a 0 into a 1? -- I'm a bit confused. (BTW: there was a also typo left from my last edit which I fixed now.)Jareb
@Wolf: Right :-) , that has nothing to do with that. Read the comments in my answer (about "calling syntax"), there I have explained the parameter. It works better on my machines with the 1 setting, so I changed that parameter as well. Some might use 0 on their PC depending on their enterprise environment.Feathered
Why is "exit /B" required? If I execute the batch from command prompt, it seems that it does not continue to ":gotPrivileges" until I REM that line. When starting the batch from the explorer by simple double-click, it works fine with the "exit /B" enabled.Absentminded
@Rev1.0 - To get elevated privileges, the batch script needs to invoke itself (a subroutine would not be enough for the UAC to be triggered). The command Exit /b ensures that only the called instance exits correctly after the elevation mechanism is invoked.Feathered
There was a problem with set "batchPath=%~0"... wscript could not find the file. I replaced that line with set "batchPath=%~dpnx0" to set the batchPath variable with the full path of the script.Nether
@MiguelAngelo - What does %~dpnx0 do exactly? According to this there is %~dp0 for drive letter and path only and %~nx0 for file name and extension only. Does it combine both? And could you give an example what is different in the path you're using that breaks the %~0 parameter? I am curious to hear what the issue is because on my machine it works fine so far.Feathered
@Feathered When I ran it here, it didn't find the path, so I debugged and found out the batchPath variable only had the file name of the batch file. So I used dpnx to specify the parts of the path that I wanted to include: d - drive, p - path, n - name and x - extension. The ~ is used to remove enclosing double quotes, if param 0 have these e.g. when it contains spaces, so this is correct. I think any combo of ~, d, p, n and x will work. The parameter 0 is defined by the caller, I think, so if the caller specify a relative path, it will only contain the relative string when the script reads it.Nether
@MiguelAngelo - I've done some testing, with Windows 10 for me it is returning exactly the same (i.e. the full path with filename and extension of the batch file executed) whether I run it with set "batchPath=%~0" or with set "batchPath=%~dpnx0" (I have added an echo %batchPath% right after the set command). Which version of Windows are you using? Windows 11?Feathered
@MiguelAngelo - I have added a remark to the listing, so those encountering the same issue as you have a workaround at hand.Feathered
@Feathered - I am using Windows 11Nether
@MiguelAngelo - I've tested it now with Windows 11, there it is also working as with 10, didn't see the issue.Feathered
@Feathered To reproduce create a file test.cmd containing echo %~0. Then, run cmd (not powershell) and go to the folder where the file is and run the script with the command test.cmd.Nether
@MiguelAngelo - That helped, thank you! Interesting issue, I could not retrace it in the first place because it only shows up if you run the script in the command shell - I double clicked on the script from the Windows Explorer, then it is showing the full path for both %~0 and %~dpnx0 (also if you "shell execute" it from notepad++ like I did). So the command shell behaves differently. I'll put in %~dpnx0 because it works in every tested scenario the same way. Good catch!Feathered
Is there a way to make this functional if the path the batch file is in contains an Ampersand?Briny
@Briny - Please check out the discusssion about ampersand in batch files here: stackoverflow.com/questions/4087695/…Feathered
L
64

As jcoder and Matt mentioned, PowerShell made it easy, and it could even be embedded in the batch script without creating a new script.

I modified Matt's script:

:: Check privileges 
net file 1>NUL 2>NUL
if not '%errorlevel%' == '0' (
    powershell Start-Process -FilePath "%0" -ArgumentList "%cd%" -verb runas >NUL 2>&1
    exit /b
)

:: Change directory with passed argument. Processes started with
:: "runas" start with forced C:\Windows\System32 workdir
cd /d %1

:: Actual work
Lacewing answered 9/7, 2014 at 23:22 Comment(7)
You are right, if PowerShell is installed, you can use it to run the batch file with elevation (thank you for the code snippet!). And yes, the label is is not needed. Thank you for the hints, it's worth a +1 ... :-)Feathered
When invoked from cmd Powershell.exe does not have -verb runas option. It does exist if you are already in PowerShell.Trinitrophenol
I really like this solution, works great for me. On Windows 8.1 I did require the :gotPrivileges label for it to work.Dread
I'm hitting a problem if this batch file is remote on a UNC path.Challis
I added change directory on start, simplified the flow and cleaned a bit. Work dir can't be changed through WorkingDirectory parameter in Start-Process because of security reason inrunas processesBlooper
Works like a charm, thank you very much. However, the cd /d %1 line should be inside the above parenthesis, just after the exit /b.Kass
@Kass I think that's incorrect. The point of the powershell Start-Process line is to re-run the current script with elevated privileges, in a new process. That process would not enter the if statement, so if the cd line were in there, it wouldn't be hit like it needs to be.Shod
R
45

I do it this way:

NET SESSION
IF %ERRORLEVEL% NEQ 0 GOTO ELEVATE
GOTO ADMINTASKS

:ELEVATE
CD /d %~dp0
MSHTA "javascript: var shell = new ActiveXObject('shell.application'); shell.ShellExecute('%~nx0', '', '', 'runas', 1);close();"
EXIT

:ADMINTASKS
(Do whatever you need to do here)
EXIT

This way it's simple and use only windows default commands. It's great if you need to redistribute you batch file.

CD /d %~dp0 Sets the current directory to the file's current directory (if it is not already, regardless of the drive the file is in, thanks to the /d option).

%~nx0 Returns the current filename with extension (If you don't include the extension and there is an exe with the same name on the folder, it will call the exe).

There are so many replies on this post I don't even know if my reply will be seen.

Anyway, I find this way simpler than the other solutions proposed on the other answers, I hope it helps someone.

Repetitive answered 7/6, 2016 at 2:40 Comment(12)
cd %~dp0 will not work on Network drives, use pushd %~dp0 instead.Sheronsherourd
I love the simplicity of this answer. And finally a reason to use Jscript!Hahnke
Do you know the /d option of the CD command? (BTW: you have a tasty piano picture in you profile)Jareb
@Jareb You can find documentation on the CD command here (as well as other commands): technet.microsoft.com/en-us/library/bb490875.aspx The /d command changes the current drive (or entire path) you're working with. It's pretty much the same as not explicitly using /d apparently.Repetitive
I already know this, I just want to emphasize Stavm's comment. You have a "current" working directory on each drive: the cd command doesn't change the drive (even if you explicitly include the drive at the beginning of the path), whereas typing the drive letter plus colon does it. The /d option includes this change of the current drive.Jareb
@Jareb Oh ok. Is that you mentioned my profile picture, I thought you were asking me hahaRepetitive
Maybe adding the /d switch would make your answer applicable also to scripts started from memory sticks and all drives other than the system drive. (concerning the profile image: I'm playing the piano and looking at this keyboard is more fun than looking on my computer keyboard.)Jareb
@Jareb I typed a huge answer only to realize I mis-understood you... I got what you mean now. I'm gonna edit it to include the /d. Thank you pal :) (P.S.: Pianist here too!)Repetitive
I tried Stavm's comment, and pushd wasn't helping much. What did is replacing the MSHTA line of the batch file (which I placed between pushd %~dp0% and popd) with these 3 lines: SET DPZERO=%~dp0% and SET NXZERO=%~nx0% and (the rest of this, up through the quotation mark, is one long line) MSHTA "javascript: var oShellApp = new ActiveXObject('Shell.Application'); var oWSH = new ActiveXObject('WScript.Shell'); oShellApp.ShellExecute('CMD', '/C ' + oWSH.Environment("Process")("DPZERO") + oWSH.Environment("Process")("NXZERO"), '', 'runas', 1);close();"Designing
(My prior comment probably only works for paths without spaces. Otherwise, quotation mark escaping is likely needed.)Designing
This works great, but it might be a good idea to move the cd command to the start (this insures that the path is also available to the elevated script - otherwise the elevated script just runs from system32). You should also redirect the net command to nul to hide it's output: net session >nul 2>&1Merissa
Funny, but Windows Defender detects this as a trojan and quarantines it on launch...Venita
C
30

I am using Matt's excellent answer, but I am seeing a difference between my Windows 7 and Windows 8 systems when running elevated scripts.

Once the script is elevated on Windows 8, the current directory is set to C:\Windows\system32. Fortunately, there is an easy workaround by changing the current directory to the path of the current script:

cd /d %~dp0

Note: Use cd /d to make sure drive letter is also changed.

To test this, you can copy the following to a script. Run normally on either version to see the same result. Run as Admin and see the difference in Windows 8:

@echo off
echo Current path is %cd%
echo Changing directory to the path of the current script
cd %~dp0
echo Current path is %cd%
pause
Comeuppance answered 3/8, 2013 at 22:35 Comment(3)
Good hint, Stephen. So the script should end with cd %~dp0 to retain its current path (I assume this works in Win7 as well, so the same command can be used although only needed for Win8+). +1 for this!Feathered
Of note this was also required on my system running Windows 7.Anvers
or use pushd %~dp0 instead... why? because popdIntendance
C
26

Matt has a great answer, but it strips away any arguments passed to the script. Here is my modification that keeps arguments. I also incorporated Stephen's fix for the working directory problem in Windows 8.

@ECHO OFF
setlocal EnableDelayedExpansion

::net file to test privileges, 1>NUL redirects output, 2>NUL redirects errors
NET FILE 1>NUL 2>NUL
if '%errorlevel%' == '0' ( goto START ) else ( goto getPrivileges ) 

:getPrivileges
if '%1'=='ELEV' ( goto START )

set "batchPath=%~f0"
set "batchArgs=ELEV"

::Add quotes to the batch path, if needed
set "script=%0"
set script=%script:"=%
IF '%0'=='!script!' ( GOTO PathQuotesDone )
    set "batchPath=""%batchPath%"""
:PathQuotesDone

::Add quotes to the arguments, if needed.
:ArgLoop
IF '%1'=='' ( GOTO EndArgLoop ) else ( GOTO AddArg )
    :AddArg
    set "arg=%1"
    set arg=%arg:"=%
    IF '%1'=='!arg!' ( GOTO NoQuotes )
        set "batchArgs=%batchArgs% "%1""
        GOTO QuotesDone
        :NoQuotes
        set "batchArgs=%batchArgs% %1"
    :QuotesDone
    shift
    GOTO ArgLoop
:EndArgLoop

::Create and run the vb script to elevate the batch file
ECHO Set UAC = CreateObject^("Shell.Application"^) > "%temp%\OEgetPrivileges.vbs"
ECHO UAC.ShellExecute "cmd", "/c ""!batchPath! !batchArgs!""", "", "runas", 1 >> "%temp%\OEgetPrivileges.vbs"
"%temp%\OEgetPrivileges.vbs" 
exit /B

:START
::Remove the elevation tag and set the correct working directory
IF '%1'=='ELEV' ( shift /1 )
cd /d %~dp0

::Do your adminy thing here...
Cathe answered 12/2, 2015 at 0:43 Comment(5)
This is a useful answer. However in ECHO UAC.ShellExecute.... line, "batchArgs!" will not expand variable. Use "!batchArgs!". My edit was rejected, so I comment.Schecter
@Schecter well spotted! I'm not sure why your edit was rejected because it was definitely a typo and your fix works. Made the change myself and tested it on Win 8.1. Now to find all the scripts where I use this code....Cathe
The script broke when using quoted arguments, like test.bat "a thing" or "test script.bat" arg1 arg2. All fixed now.Cathe
I managed to break it (due to my fault: running script from mapped network drive, since admin and normal user dont have same mapping). Still: is there a way to see the output? For me, I had to find the .vbs and change the /c to a /K and then saw it manually.Claudio
@jxmallet - just replace '!arg!' to '%arg%' and you can use double quoted arguments. this work for me...Vibraphone
D
25

You can have the script call itself with psexec's -h option to run elevated.

I'm not sure how you would detect if it's already running as elevated or not... maybe re-try with elevated perms only if there's an Access Denied error?

Or, you could simply have the commands for the xcopy and reg.exe always be run with psexec -h, but it would be annoying for the end-user if they need to input their password each time (or insecure if you included the password in the script)...

Duchy answered 12/8, 2011 at 19:4 Comment(6)
Thanks for the response. Unfortunately, I don't think I can use anything outside of stock Windows Vista/7 tools because this will be going out to customers outside of my office. I don't think I can legally distribute PSExec.Piscatorial
Yup, I think you are right about that--even though PSExec is now a Microsoft tool (since they bought out the Sysinternals guys!) the EULA does forbid distribution :(Duchy
I think my options are pretty limited. If I knew how to code in VB, I could make it an exe with an admin manifest, but I wouldn't even know where to start. I guess I'll just warn at the beginning of the batch to run as admin if they're running Windows Vista/7. Thanks all.Piscatorial
Another 3rd-party tool that might be freely redistributable and easy to integrate and learn is AutoIt; this page demonstrates how the script requests elevated privileges.Duchy
Thanks ewall. It looks like I can redistribute AutoIt. I'll go down that road. Thanks again for the help!Piscatorial
psexec -h doesn't work: 'Couldn't install PSEXESVC service: Access is denied.'. You need to already have the administrator rights to run psexec.Nureyev
T
24

I use PowerShell to re-launch the script elevated if it's not. Put these lines at the very top of your script.

net file 1>nul 2>nul && goto :run || powershell -ex unrestricted -Command "Start-Process -Verb RunAs -FilePath '%comspec%' -ArgumentList '/c %~fnx0 %*'"
goto :eof
:run
:: TODO: Put code here that needs elevation

I copied the 'net name' method from @Matt's answer. His answer is much better documented and has error messages and the like. This one has the advantage that PowerShell is already installed and available on Windows 7 and up. No temporary VBScript (*.vbs) files, and you don't have to download tools.

This method should work without any configuration or setup, as long as your PowerShell execution permissions aren't locked down.

Teter answered 10/9, 2014 at 3:28 Comment(6)
when providing a list whitespace seperated arguments surrounded by quotes to get it treated as one, the /c %~fnx0 %*' part seems to leave every part besides the first. Eg from test.bat "arg1 arg2 arg3" only arg1 is passed forwardRentschler
It seems that no matter what, the Start-Process removes all double quotes in the argumentlist..Rentschler
Works for me! I'm using it for netsh int set int %wifiname% Enable/Disable.Tequila
I had the same problem as Nicolas Mommaerts above. I don't understand why it works (the best kind of fix), but the following works like it should (note all the extra quotation marks at the end): net file 1>nul 2>nul && goto :run || powershell -ex unrestricted -Command "Start-Process -Verb RunAs -FilePath '%comspec%' -ArgumentList '/c ""%~fnx0""""'"Maneater
Another problem (inconvenience) is that the script pauses (waits for user input) if the server service isn't running (i have it disabled for reasons). I usuallly test for admin perms by trying to write to the HOSTS file location, but it's perhaps better to invoke the powershell command -Verb runas, instead of relying on enabled windows services or attempting file writes.Metaphrase
@Maneater +1 for your quotes fix. I can't shed any light on why it works either (seems mismatched to me!) but your version knocks off the " %* " from the original. Intentional? This is presumably designed to relaunch the script with the same parameters as the original invocation. For quotes around the script name (so it can contain spaces), the following seems to work: ""%~fnx0"""" %*'". If there are quotes in the other parameters though, they are still stripped.Gillead
B
16

For some programs setting the super secret __COMPAT_LAYER environment variable to RunAsInvoker will work.Check this :

set "__COMPAT_LAYER=RunAsInvoker"
start regedit.exe

Though like this there will be no UAC prompting the user will continue without admin permissions.

Badgett answered 29/3, 2016 at 18:55 Comment(3)
The link to the Microsoft site no longer worked, so I changed it to the content on archive.org. If anyone can find a working link to the content on Microsoft's site, but all means, please update it to that.Dube
it's still in medium integrity not elevated, so you can't edit HKLM in regedit. It just skipped the uac.Posology
set "__COMPAT_LAYER=RunAsAdminitrator" --- will enable UAC dialog;Haupt
N
12

I wrote gsudo, a sudo for windows: that elevates in the current console (no context switching to a new window), with a credentials cache (reduced UAC popups), and also elevates PowerShell commands.

It allows to elevate commands that require admin privileges, or the whole batch, if you want. Just prepend gsudo before anything that needs to run elevated.

Example batch file that elevates itself using gsudo:

EDIT: New one liner version that works with any windows language and avoids whoami issues:

net session >nul 2>nul & net session >nul 2>nul || gsudo "%~f0" && exit /b || exit /b
:: This will run as admin ::

Alternative (original version):

@echo off
  rem Test if current context is already elevated:
  whoami /groups | findstr /b BUILTIN\Administrators | findstr /c:"Enabled group" 1> nul 2>nul && goto :isadministrator
  echo You are not admin. (yet)
  :: Use gsudo to launch this batch file elevated.
  gsudo "%~f0"
  goto end
:isadministrator
  echo You are admin.
  echo (Do admin stuff now).
:end

Install:

See gsudo in action: gsudo demo

Nashoma answered 3/1, 2020 at 21:18 Comment(7)
Cool. Thanks. On which versions of Windows does it function?Dube
The tests suite runs on Server 2019 and my local dev box is Windows 10 1909. Sorry I haven't tested compatibility with other older OS.Nashoma
Thanks Gerardo. If anyone tries it on other versions of Windows, please post your results.Dube
I've just tested gsudo v0.7 on Windows 8.1 and worked. It's not trivial to install .Net Framework 4.6 there. Luckily choco install dotnet4.6.2 brought some hard-to-get dependencies. Then gsudo config Prompt "$p# " fixed an issue with ConHost showing a wrong prompt (escape sequence chars instead of the red sharp). @RockPaperLizardNashoma
Thanks so much Gerardo. Could it be recompiled to work with any versions of .Net Framework before 4.6, or does it depend on some functionality specific to 4.6?Dube
Does not depend on 4.6 functionality. Could be backported, but the code uses some 'modern' C# syntax sugar that probably wont compile without fixing.Nashoma
This is great tools, Thanks very muchCrispi
P
11

I recently needed a user-friendly approach and I came up with this, based on valuable insights from contributors here and elsewhere. Simply put this line at the top of your .bat script. Feedback welcome.

@pushd %~dp0 & fltmc | find "." && (powershell start '%~f0' ' %*' -verb runas 2>nul) && (popd & exit /b)

Intrepretation:

  • @pushd %~dp0 ensures a consistent working directory; supports UNC paths

  • & fltmc runs a native windows command that outputs an error when run unelevated

  • | find "." makes that error prettier, and causes nothing to output when elevated

  • && ( if we successfully got an error because we're not elevated, do this...

  • powershell start invoke PowerShell and call the Start-Process cmdlet (start is an alias)

  • '%~f0' pass in the full path and name of this .bat file. Single quotes allow for spaces

  • ' %*' pass in any and all arguments to this .bat file. Funky quoting and escape sequences probably won't work, but simple quoted strings should. The leading space is needed to prevent breaking things if no arguments are present

  • -verb runas don't just start the process... RunAs Administrator!

  • 2>nul) discard PowerShell's unsightly error output if the UAC prompt is canceled/ignored

  • && if we successfully invoked ourself with PowerShell, then...

    NOTE: in the event we don't obtain elevation (user cancels UAC) then the && here allows the .bat to continue running without elevation, such that any commands that require it will fail but others will work just fine. If you want the script to simply exit instead of running unelevated, make this a single ampersand: &

  • (popd & exit /b) returns to the initial working directory on the command line and exits the initial .bat processing, because we don't need it anymore; we already have an elevated process running this .bat. The /b switch allows cmd.exe to remain open if the .bat was started from the command line – this has no effect if the .bat was double-clicked

Ponderous answered 10/2, 2021 at 6:9 Comment(3)
Nicely done! Glad to see good answers to this question are still flowing in.Dube
doesnt seem to work with me as is, but I could change the . to a : in the find, because apparently the error message in german didnt have a dot.Motmot
I would suggest changing ... && exit /b) to ... && popd && exit /b). that way the current directory keeps unchanged after running the script.Tuberculosis
D
8

When a CMD script needs Administrator rights and you know it, add this line to the very top of the script (right after any @ECHO OFF):

NET FILE > NUL 2>&1 || POWERSHELL -ex Unrestricted -Command "Start-Process -Verb RunAs -FilePath '%ComSpec%' -ArgumentList '/c \"%~fnx0\" %*'" && EXIT /b

The NET FILE checks for existing Administrator rights. If there are none, PowerShell starts the current script (with its arguments) in an elevated shell, and the non-elevated script closes.

Demise answered 21/9, 2021 at 10:19 Comment(2)
Nice technique! Upvoted. If admin rights aren't granted, will this generate an infinite loop?Dube
No, the script will just (give an error and) try without elevation.Demise
K
7

If you don’t care about arguments then here’s a compact UAC prompting script that’s a single line long. It doesn’t pass arguments through since there’s no foolproof way to do that that handles every possible combination of poison characters.

net sess>nul 2>&1||(echo(CreateObject("Shell.Application"^).ShellExecute"%~0",,,"RunAs",1:CreateObject("Scripting.FileSystemObject"^).DeleteFile(wsh.ScriptFullName^)>"%temp%\%~nx0.vbs"&start wscript.exe "%temp%\%~nx0.vbs"&exit)

Paste this line under the @echo off in your batch file.

Explanation

The net sess>nul 2>&1 part is what checks for elevation. net sess is just shorthand for net session which is a command that returns an error code when the script doesn’t have elevated rights. I got this idea from this SO answer. Most of the answers here feature net file instead though which works the same. This command is fast and compatible on many systems.

The error level is then checked with the || operator. If the check succeeds then it creates and executes a WScript which re-runs the original batch file but with elevated rights before deleting itself.


Alternatives

The WScript file is the best approach being fast and reliable, although it uses a temporary file. Here are some other variations and their dis/ad-vantages.

PowerShell

net sess>nul 2>&1||(powershell saps '%0'-Verb RunAs&exit)

Pros:

  • Very short.
  • No temporary files.

Cons:

  • Slow. PowerShell can be slow to start up.
  • Spews red text when the user declines the UAC prompt. The PowerShell command could be wrapped in a try{...}catch{} to prevent this though.

Mshta WSH script

net sess>nul 2>&1||(start mshta.exe vbscript:code(close(Execute("CreateObject(""Shell.Application"").ShellExecute""%~0"",,,""RunAs"",1"^)^)^)&exit)

Pros:

  • Fast.
  • No temporary files.

Cons:

  • Not reliable. Some Windows 10 systems will block the script from running due to Windows Defender intercepting it as a potential trojan.
Koppel answered 1/7, 2020 at 1:44 Comment(4)
this line is magical, can you explain the first section net sess>nul 2>&1?Microsporophyll
Nice solution but regarding the first method, when I try to pass any argument to my batch file, it doesn't reach to that batch file.... I tried to play around with ShellExecute"%fullPathArgs%" but didn't work either. It seems that I've to pass the batch file arguments in another parameter.Conti
@Conti If the arguments are simple you could use ShellExecute"%~0","%~*",,"RunAs" instead of ShellExecute"%~0",,,"RunAs", but complicated arguments involving quoting, escape sequences, or poison characters will break it. Notice that other answers will have similar caveats. Preserving the arguments reliably is not an easy task.Koppel
@Microsporophyll It just checks for admin rights (NET SESS), silently: output is ignored (>NUL), as is the error stream by redirecting it to standard output (2>&1).Demise
I
5

I pasted this in the beginning of the script:

:: BatchGotAdmin
:-------------------------------------
REM  --> Check for permissions
>nul 2>&1 "%SYSTEMROOT%\system32\icacls.exe" "%SYSTEMROOT%\system32\config\system"

REM --> If error flag set, we do not have admin.
if '%errorlevel%' NEQ '0' (
    echo Requesting administrative privileges...
    goto UACPrompt
) else ( goto gotAdmin )

:UACPrompt
    echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
    echo args = "" >> "%temp%\getadmin.vbs"
    echo For Each strArg in WScript.Arguments >> "%temp%\getadmin.vbs"
    echo args = args ^& strArg ^& " "  >> "%temp%\getadmin.vbs"
    echo Next >> "%temp%\getadmin.vbs"
    echo UAC.ShellExecute "%~s0", args, "", "runas", 1 >> "%temp%\getadmin.vbs"

    "%temp%\getadmin.vbs" %*
    exit /B

:gotAdmin
    if exist "%temp%\getadmin.vbs" ( del "%temp%\getadmin.vbs" )
    pushd "%CD%"
    CD /D "%~dp0"
:--------------------------------------
Iraqi answered 11/8, 2014 at 8:2 Comment(3)
I like the arg processing in your script. But note that cacls is deprecated in Windows 7 and newer windows versions.Feathered
Fsutil dirty is even betterJareb
Do you know that the line pushd "%CD%" saves the current working directory and changes to the current working directory?Jareb
C
4

Although not directly applicable to this question, because it wants some information for the user, google brought me here when I wanted to run my .bat file elevated from task scheduler.

The simplest approach was to create a shortcut to the .bat file, because for a shortcut you can set Run as administrator directly from the advanced properties.

Running the shortcut from task scheduler, runs the .bat file elevated.

Carliecarlile answered 24/7, 2019 at 15:13 Comment(0)
S
4

Using powershell.

If the cmd file is long I use a first one to require elevation and then call the one doing the actual work.

If the script is a simple command everything may fit on one cmd file. Do not forget to include the path on the script files.

Template:

@echo off
powershell -Command "Start-Process 'cmd' -Verb RunAs -ArgumentList '/c " comands or another script.cmd go here "'"

Example 1:

@echo off
powershell -Command "Start-Process 'cmd' -Verb RunAs -ArgumentList '/c "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\BIN\x.ps1"'"

Example 2:

@echo off
powershell -Command "Start-Process 'cmd' -Verb RunAs -ArgumentList '/c "c:\bin\myScript.cmd"'"
Sissified answered 4/1, 2020 at 19:16 Comment(1)
Side note: you don't need to proxy through cmd. You can place the executable there and only the arguments at the end, as in - powershell -Command "Start-Process 'docker-compose' -Verb RunAs -ArgumentList '"-f ./build/postgres.yaml up"'"Udometer
C
3

One-liner batch user elevation (with arguments)

Here is my one-liner version for this age-old question of batch user elevation which is still relevant today.
Simply add the code to the top of your batch script and you're good to go.

Silent

This version does not output anything nor pause execution on error.

@setlocal disabledelayedexpansion enableextensions
@echo off

:: Admin check
fltmc >nul 2>nul || set _=^"set _ELEV=1^& cd /d """%cd%"""^& "%~f0" %* ^"&&((if "%_ELEV%"=="" ((powershell -nop -c start cmd -args '/d/x/s/v:off/r',$env:_ -verb runas >nul 2>nul) || (mshta vbscript:execute^("createobject(""shell.application"").shellexecute(""cmd"",""/d/x/s/v:off/r ""&createobject(""WScript.Shell"").Environment(""PROCESS"")(""_""),,""runas"",1)(window.close)"^) >nul 2>nul)))& exit /b)
Verbose

A verbose version which tells the user that admin privileges are being requested and pauses on error before exiting.

@setlocal disabledelayedexpansion enableextensions
@echo off

:: Admin check
fltmc >nul 2>nul || set _=^"set _ELEV=1^& cd /d """%cd%"""^& "%~f0" %* ^"&&((if "%_ELEV%"=="" (echo Requesting administrator privileges...&((powershell -nop -c start cmd -args '/d/x/s/v:off/r',$env:_ -verb runas >nul 2>nul) || (mshta vbscript:execute^("createobject(""shell.application"").shellexecute(""cmd"",""/d/x/s/v:off/r ""&createobject(""WScript.Shell"").Environment(""PROCESS"")(""_""),,""runas"",1)(window.close)"^) >nul 2>nul))) else (echo This script requires administrator privileges.& pause))& exit /b)

echo Has admin permissions
echo Working dir: "%cd%"
echo Script dir: "%~dp0"
echo Script path: "%~f0"
echo Args: %*

pause

Method of operation

  1. Uses fltmc to check for administrator privileges. (system component, included in Windows 2000+)
  2. If user already has administrator privileges, continues operation normally.
  3. If not, spawns an elevated version of itself using either:
    1. powershell (optional Windows feature, included in Windows 7+ by default, can be uninstalled/otherwise not available, can be installed on Windows XP/Vista)
    2. mshta (system component, included in Windows 2000+)
  4. If fails to acquire elevation, stops execution (instead of looping endlessly).

What sets this solution apart from others?

There are literally hundreds of variations around for solving this issue but everything I've found so far have their shortcomings and this is an attempt of solving most of them.

  • Compatibility. Using fltmc as the means of checking for privileges and either powershell or mshta for elevation works with every Windows version since 2000 and should cover most system configurations.
  • Does not write any extra files.
  • Preserves current working directory. Most of the solutions found conflate "script directory" with "working directory" which are totally different concepts. If you want to use "script directory" instead, replace %cd% with %~dp0. Some people advocate using pushd "%~dp0" instead so paths inside networked UNC paths like "\\SOMEONES-PC\share" will work but that will also automagically map that location to a drive letter (like Y:) which might or might not be what you want.
  • Stops if unable to acquire elevation. This can happen because of several reasons, like user clicking "No" on the UAC prompt, UAC being disabled, group policy settings, etc. Many other solutions enter an endless loop on this point, spawning millions of command prompts until the heat death of the universe.
  • Supports (most of) command-line arguments and weird paths. Stuff like ampersands &, percent signs %, carets ^ and mismatching amount of quotes """'. You still definitely CAN break this by passing a sufficiently weird combinations of those, but that is an inherent flaw of Windows' batch processing and cannot really be worked around to always work with any combination. Most typical use-cases should be covered though and arguments work as they would without the elevation script.

Known issues

If you enter a command-line argument that has a mismatched amount of double-quotes (i.e. not divisible by 2), an extra space and a caret ^ will be added as a last argument. For example "arg1" arg2" """" "arg3" will become "arg1" arg2" """" "arg3" ^. If that matters for your script, you can add logic to fix it, f.ex. check if _ELEV=1 (meaning that elevation was required) and then check if the last character of argument list is ^ and/or amount of quotes is mismatched and remove the misbehaving caret.

Example script for logging output to file

You cannot easily use > for stdout logging because on elevation a new cmd window is spawned and execution context switched.

You can achieve it by passing increasingly weird combinations of escape characters, like elevate.bat testarg ^^^> test.txt but then you would need to make it always spawn the new cmd window or add logic to strip out the carets, all of which increases complexity and it would still break in many scenarios.

The best and easiest way would be simply adding the logging inside your batch script, instead of trying to redirect from command line. That'll save you a lot of headache.

Here is an example how you can easily implement logging for your script:

@setlocal disabledelayedexpansion enableextensions
@echo off

:: Admin check
fltmc >nul 2>nul || set _=^"set _ELEV=1^& cd /d """%cd%"""^& "%~f0" %* ^"&&((if "%_ELEV%"=="" (echo Requesting administrator privileges...&((powershell -nop -c start cmd -args '/d/x/s/v:off/r',$env:_ -verb runas >nul 2>nul) || (mshta vbscript:execute^("createobject(""shell.application"").shellexecute(""cmd"",""/d/x/s/v:off/r ""&createobject(""WScript.Shell"").Environment(""PROCESS"")(""_""),,""runas"",1)(window.close)"^) >nul 2>nul))) else (echo This script requires administrator privileges.& pause))& exit /b)

set _log=
set _args=%*
if not defined _args goto :noargs
set _args=%_args:"=%
set _args=%_args:(=%
set _args=%_args:)=%
for %%A in (%_args%) do (if /i "%%A"=="-log" (set "_log=>> %~n0.log"))
:noargs

if defined _log (echo Logging to file %~n0.log) else (echo Logging to stdout)
echo Has admin permissions %_log%
echo Working dir: "%cd%" %_log%
echo Script dir: "%~dp0" %_log%
echo Script path: "%~f0" %_log%
echo Args: %* %_log%

echo Hello World! %_log%

pause

Run: logtest.bat -log By adding argument -log , the output will be logged to a file instead of stdout.

Closing thoughts

It bewilders me how a simple "ELEVATE" instruction has not been introduced to batch even after 15 years of UAC existing. Maybe one day Microsoft will get their shit together. Until then, we have to resort to using these hacks.

Culler answered 20/4, 2022 at 10:52 Comment(1)
Concur with the struggle to understand universal ELEVATE. Though it does exist in various powertoys and 3rd party tools. Why M$ never did a SETGID flag similar to Unix like systems (chmod u=rwx,g+x,ug+s <file> or chmod 6710 <file>) - Which would be simple and logical to implement through Windows extended file attributes. (BTW that chmod translates to: "If user is a member of the <file> group then adopt admin's group on execution") - Linux also has extended attributes to allow more complex permissions.Pawnshop
D
1

Just one line will be enough, place it on the first line of your script to make the script ran with administrator privileges:

net file 1>NUL 2>NUL || start "" mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe","/c pushd ""%~dp0"" && ""%~s0"" %*","","runas",0)(window.close) && exit

If you are using Win 10 and above, or if VBScript got removed in the future releases of Windows, then you can use:

net file 1>NUL 2>NUL || powershell Start-Process -FilePath cmd.exe -ArgumentList """/c pushd %~dp0 && %~s0 %*""" -Verb RunAs && exit

================================================

Can support:

  • Script path that has spaces
  • Automatically switch to the path where the script is located
  • Provide script arguments
  • Hidden window (see below)

If you do not need to hide the window, for the mshta version, you only need to change 0 to 1:

net file 1>NUL 2>NUL || start "" mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe","/c pushd ""%~dp0"" && ""%~s0"" %*","","runas",1)(window.close) && exit

For the powershell version, you can simply add -WindowStyle Hidden in it:

net file 1>NUL 2>NUL || powershell Start-Process -FilePath cmd.exe -ArgumentList """/c pushd %~dp0 && %~s0 %*""" -Verb RunAs -WindowStyle Hidden && exit

Refers:

Dunc answered 16/11, 2022 at 2:41 Comment(1)
This is clever. Can you add an explanation so everyone can understand what you are doing here?Dube
M
0

Try this:

@echo off
CLS
:init
setlocal DisableDelayedExpansion
set cmdInvoke=1
set winSysFolder=System32
set "batchPath=%~0"
for %%k in (%0) do set batchName=%%~nk
set "vbsGetPrivileges=%temp%\OEgetPriv_%batchName%.vbs"
setlocal EnableDelayedExpansion
:checkPrivileges
NET FILE 1>NUL 2>NUL
if '%errorlevel%' == '0' ( goto gotPrivileges ) else ( goto getPrivileges )
:getPrivileges
if '%1'=='ELEV' (echo ELEV & shift /1 & goto gotPrivileges)
ECHO.
ECHO Set UAC = CreateObject^("Shell.Application"^) > "%vbsGetPrivileges%"
ECHO args = "ELEV " >> "%vbsGetPrivileges%"
ECHO For Each strArg in WScript.Arguments >> "%vbsGetPrivileges%"
ECHO args = args ^& strArg ^& " "  >> "%vbsGetPrivileges%"
ECHO Next >> "%vbsGetPrivileges%"
if '%cmdInvoke%'=='1' goto InvokeCmd 
ECHO UAC.ShellExecute "!batchPath!", args, "", "runas", 1 >> "%vbsGetPrivileges%"
goto ExecElevation
:InvokeCmd
ECHO args = "/c """ + "!batchPath!" + """ " + args >> "%vbsGetPrivileges%"
ECHO UAC.ShellExecute "%SystemRoot%\%winSysFolder%\cmd.exe", args, "", "runas", 1 >> "%vbsGetPrivileges%"
:ExecElevation
"%SystemRoot%\%winSysFolder%\WScript.exe" "%vbsGetPrivileges%" %*
exit /B
:gotPrivileges
setlocal & cd /d %~dp0
if '%1'=='ELEV' (del "%vbsGetPrivileges%" 1>nul 2>nul  &  shift /1)
REM Run shell as admin (example) - put here code as you like
ECHO %batchName% Arguments: P1=%1 P2=%2 P3=%3 P4=%4 P5=%5 P6=%6 P7=%7 P8=%8 P9=%9
cmd /k

If you need information on that batch file, run the HTML/JS/CSS Snippet:

document.getElementsByTagName("data")[0].innerHTML="ElevateBatch, version 4, release<br>Required Commands:<ul><li>CLS</li><li>SETLOCAL</li><li>SET</li><li>FOR</li><li>NET</li><li>IF</li><li>ECHO</li><li>GOTO</li><li>EXIT</li><li>DEL</li></ul>It auto-elevates the system and if the user presses No, it just doesn't do anything.<br>This CANNOT be used to create an Elevated Explorer.";
data{font-family:arial;text-decoration:none}
<data></data>
Mickiemickle answered 21/2, 2020 at 19:52 Comment(0)
K
-4

Following solution is clean and works perfectly.

  1. Download Elevate zip file from https://www.winability.com/download/Elevate.zip

  2. Inside zip you should find two files: Elevate.exe and Elevate64.exe. (The latter is a native 64-bit compilation, if you require that, although the regular 32-bit version, Elevate.exe, should work fine with both the 32- and 64-bit versions of Windows)

  3. Copy the file Elevate.exe into a folder where Windows can always find it (such as C:/Windows). Or you better you can copy in same folder where you are planning to keep your bat file.

  4. To use it in a batch file, just prepend the command you want to execute as administrator with the elevate command, like this:

 elevate net start service ...
Kayseri answered 24/9, 2018 at 17:29 Comment(2)
That command only opens a dialogwindow what asks the user if this command is allowed to be executed. So you cant use this command in a batchfile what should run WITHOUT user interactions.Arnuad
If it were possible to elevate WITHOUT user interaction it would be a huge security hole, wouldn't it? Every virus would start using that method to elevate itself without user approval.Cockatrice

© 2022 - 2024 — McMap. All rights reserved.