267

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.

Matt
  • 25,467
  • 18
  • 120
  • 187
PDixon724
  • 2,673
  • 3
  • 15
  • 4
  • 2
    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. – Matt Sep 04 '12 at 13:34
  • 1
    Please consider if Matt's answer would be the ticked one? Seems so to me. – akauppi Mar 31 '15 at 04:56
  • 1
    possible duplicate of [How to request Administrator access inside a batch file](http://stackoverflow.com/questions/1894967/how-to-request-administrator-access-inside-a-batch-file) – Jim Fell Aug 11 '15 at 18:24
  • Please regard the new **Windows 10** hints in the comments section of the [batch script](http://stackoverflow.com/a/12264592/1016343) I have posted. – Matt Oct 04 '15 at 14:54
  • 5
    From cmd: `@powershell Start-Process cmd -Verb runas`. From Powershell just drop `@powershell`. This starts cmd with elevated rights. – BrunoLM Nov 01 '15 at 18:42
  • Or try pure windows way using **schtasks**: http://stackoverflow.com/questions/19098101/how-to-open-an-elevated-cmd-using-command-line-for-windows – sactiw Apr 15 '16 at 10:52
  • 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](https://stackoverflow.com/a/60292423/12861751). – ScriptKidd Feb 22 '20 at 03:00
  • @akauppi - 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. – Matt Apr 27 '22 at 14:16

19 Answers19

365

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://stackoverflow.com/a/12264592/1016343" 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.

Matt
  • 25,467
  • 18
  • 120
  • 187
  • 19
    Great answer, although it amazes me slightly that you have to do all that to do something that is clearly necessary in some cases. – jcoder Nov 23 '12 at 10:58
  • 68
    Indeed, a command such as ELEVATE is clearly missing in the Windows batch language. – Matt Nov 23 '12 at 11:45
  • 2
    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 :( – jcoder Nov 23 '12 at 12:13
  • 1
    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). – Matt Nov 23 '12 at 13:15
  • 1
    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. – Matt Aug 22 '14 at 11:54
  • My powershell method: http://stackoverflow.com/a/25756858/821878 Not as nice as an "ELEVATE" command, but it doesn't require a temporary *.vbs. – Ryan Bemrose Sep 10 '14 at 03:29
  • 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 prompt – Nicolas Mommaerts Sep 30 '14 at 09:14
  • @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. – Matt Oct 06 '14 at 17:44
  • I have already renamed the (legacy) bat file, but thanks for the info anyway – Nicolas Mommaerts Oct 06 '14 at 19:22
  • 2
    You might want to add [`cd %~dp0`](http://serverfault.com/a/255294/157477) 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*... – Tomáš Zato May 06 '15 at 17:27
  • @Tomáš Zato: Yes, that is correct. Note that Stephen has already [suggested it.](http://stackoverflow.com/a/18037959/1016343) Take a look at the code snippet he has provided. – Matt May 07 '15 at 07:05
  • 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. – VitaminYes Oct 02 '15 at 02:31
  • @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. – Matt Oct 03 '15 at 11:45
  • 1
    @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. – Matt Oct 03 '15 at 11:59
  • 1
    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. – Matt Oct 04 '15 at 14:51
  • 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 all – VitaminYes Oct 05 '15 at 06:07
  • @VitaminYes: Have you tried the updated version of the script I provided yesterday? It should run regardless of the .vbs extension registration. – Matt Oct 05 '15 at 06:35
  • 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. – NeplatnyUdaj Oct 08 '15 at 11:34
  • @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. – Matt Oct 08 '15 at 12:07
  • 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. – NeplatnyUdaj Oct 08 '15 at 12:43
  • 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. – Matt Oct 08 '15 at 12:45
  • 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. – NeplatnyUdaj Oct 15 '15 at 13:21
  • 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. @NeplatnyUdaj – Matt Oct 15 '15 at 16:27
  • 2
    @Matt Can you check whether there is some truth behind [this rejected edit](http://stackoverflow.com/review/suggested-edits/9968170) on your answer? – Artjom B. Oct 25 '15 at 09:24
  • @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! – Matt Oct 26 '15 at 10:09
  • Is there a way to cleanup `%TEMP%` and delete `OEgetPrivileges.vbs` upon termination? – mlt May 13 '16 at 20:27
  • 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. @mlt – Matt May 16 '16 at 15:40
  • 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. – mlt May 16 '16 at 18:43
  • @mlt: I've added a cleanup to the script, it was easier than expected. Thank you for the hint! – Matt May 20 '16 at 07:41
  • 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. – Princa Jun 13 '16 at 15:15
  • @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. – Matt Jun 13 '16 at 16:22
  • 1
    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. – Troy Aug 16 '16 at 19:28
  • @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). – Matt Aug 18 '16 at 08:54
  • @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: call A.bat :: then call my custom codes So, just like A.bat is a library and I want B.bat looks simple. But this won't work for me. – Troy Aug 18 '16 at 15:16
  • 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. – Matt Aug 18 '16 at 15:31
  • @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. – Troy Aug 18 '16 at 19:44
  • 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? – Andreas Brauchle Nov 29 '16 at 08:02
  • nevermind, I used http://stackoverflow.com/a/30590134/3706972 which is working with spaces in parameters. – Andreas Brauchle Nov 29 '16 at 08:11
  • @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. – Matt Nov 29 '16 at 12:31
  • 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. – ADBailey Dec 07 '16 at 12:22
  • @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`. – Matt Dec 09 '16 at 12:44
  • Ah, `SysNative` isn't a "real" directory, just a feature of the [File System Redirector](https://msdn.microsoft.com/en-us/library/windows/desktop/aa384187(v=vs.85).aspx) 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. – ADBailey Dec 09 '16 at 17:07
  • 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. – ADBailey Dec 09 '16 at 17:17
  • @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 ... – Matt Dec 12 '16 at 08:52
  • 1
    @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. – Matt Dec 13 '16 at 12:49
  • 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). – ADBailey Dec 14 '16 at 13:18
  • @ADBailey: Thanks, I have added your explanation to the answer. – Matt Dec 14 '16 at 14:17
  • The `Elevate.cmd` script ends with `cmd /k`. Does this create a new shell process and stay there? – lit Jan 04 '17 at 16:37
  • @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. – Matt Jan 04 '17 at 18:13
  • 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](http://ss64.com/nt/pushd.html) ...and my personal observations – Wolf Feb 07 '17 at 16:00
  • @Wolf - `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. – Matt Feb 07 '17 at 16:45
  • 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 .` – Wolf Feb 07 '17 at 17:30
  • @Wolf - Thanks for the hint, I've optimized the script and removed the pushd. – Matt Feb 08 '17 at 09:53
  • 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.) – Wolf Feb 08 '17 at 10:02
  • @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. – Matt Feb 08 '17 at 11:51
  • 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. – Rev Sep 18 '18 at 09:11
  • 1
    @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. – Matt Sep 19 '18 at 09:21
  • 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. – Miguel Angelo Feb 10 '22 at 03:58
  • @MiguelAngelo - What does `%~dpnx0` do exactly? According to [this](https://stackoverflow.com/questions/5034076/what-does-dp0-mean-and-how-does-it-work) 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. – Matt Feb 10 '22 at 07:27
  • @Matt 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. – Miguel Angelo Feb 22 '22 at 19:52
  • @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? – Matt Feb 23 '22 at 08:12
  • @MiguelAngelo - I have added a remark to the listing, so those encountering the same issue as you have a workaround at hand. – Matt Feb 25 '22 at 06:59
  • @Matt - I am using Windows 11 – Miguel Angelo Feb 25 '22 at 17:57
  • @MiguelAngelo - I've tested it now with Windows 11, there it is also working as with 10, didn't see the issue. – Matt Mar 02 '22 at 07:16
  • @Matt 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`. – Miguel Angelo Mar 02 '22 at 16:10
  • 1
    @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! – Matt Mar 03 '22 at 08:14
  • Is there a way to make this functional if the path the batch file is in contains an Ampersand? – tjb1 Jul 16 '22 at 10:48
  • @tjb1 - Please check out the discusssion about ampersand in batch files here: https://stackoverflow.com/questions/4087695/escaping-ampersands-in-windows-batch-files – Matt Jul 18 '22 at 06:43
61

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
ceztko
  • 14,736
  • 5
  • 58
  • 73
Ir Relevant
  • 963
  • 6
  • 12
  • 3
    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 ... :-) – Matt Jul 28 '14 at 15:29
  • 2
    When invoked from cmd Powershell.exe does not have -verb runas option. It does exist if you are already in PowerShell. – Adil Hindistan Jul 29 '14 at 18:48
  • 1
    I really like this solution, works great for me. On Windows 8.1 I did require the :gotPrivileges label for it to work. – ShaunO Mar 27 '15 at 01:40
  • I'm hitting a problem if this batch file is remote on a UNC path. – Ryan Beesley Jun 19 '15 at 23:52
  • 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 in`runas` processes – ceztko Dec 06 '19 at 13:29
  • 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`. – braggPeaks Oct 29 '20 at 08:14
  • @braggPeaks 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. – Rabadash8820 Dec 19 '21 at 06:26
44

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.

Matheus Rocha
  • 1,152
  • 13
  • 22
  • 5
    cd %~dp0 will not work on Network drives, use pushd %~dp0 instead. – Stavm Jun 19 '16 at 08:41
  • 2
    I love the simplicity of this answer. And finally a reason to use Jscript! – Joe Coder Aug 18 '16 at 01:43
  • Do you know the `/d` option of the `CD` command? (BTW: you have a tasty piano picture in you profile) – Wolf Feb 08 '17 at 09:12
  • @Wolf You can find documentation on the `CD` command here (as well as other commands): https://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. – Matheus Rocha Feb 13 '17 at 04:26
  • I already know this, I just want to emphasize [Stavm's comment](https://stackoverflow.com/questions/7044985/how-can-i-auto-elevate-my-batch-file-so-that-it-requests-from-uac-administrator/37669661?noredirect=1#comment63263262_37669661). 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. – Wolf Feb 13 '17 at 06:00
  • @Wolf Oh ok. Is that you mentioned my profile picture, I thought you were asking me haha – Matheus Rocha Feb 13 '17 at 10:34
  • 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.) – Wolf Feb 13 '17 at 12:18
  • 2
    @Wolf 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!) – Matheus Rocha Feb 13 '17 at 13:48
  • 1
    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();"` – TOOGAM May 26 '17 at 20:00
  • (My prior comment probably only works for paths without spaces. Otherwise, quotation mark escaping is likely needed.) – TOOGAM May 26 '17 at 22:47
  • 1
    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>&1` – Nulano Aug 08 '17 at 18:03
  • 3
    Funny, but Windows Defender detects this as a trojan and quarantines it on launch... – Ola Berntsson Sep 23 '18 at 22:13
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
Wolf
  • 9,679
  • 7
  • 62
  • 108
Stephen Klancher
  • 1,374
  • 15
  • 24
  • 1
    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! – Matt Sep 17 '13 at 10:11
  • 2
    Of note this was also required on my system running Windows 7. – meh-uk Jul 24 '15 at 13:34
  • 1
    or use `pushd %~dp0` instead... why? because `popd` – Jaroslav Záruba Jul 21 '17 at 11:05
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...
isidroco
  • 3
  • 2
jxmallett
  • 4,087
  • 1
  • 28
  • 35
  • 2
    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. – fliedonion Apr 04 '15 at 14:14
  • 1
    @fliedonion 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.... – jxmallett Apr 06 '15 at 23:44
  • 3
    The script broke when using quoted arguments, like `test.bat "a thing"` or `"test script.bat" arg1 arg2`. All fixed now. – jxmallett Apr 07 '15 at 01:01
  • 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. – Andreas Reiff Oct 16 '15 at 13:46
  • @jxmallet - just replace `'!arg!'` to `'%arg%'` and you can use double quoted arguments. this work for me... – mshakurov Nov 17 '21 at 17:10
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)...

ewall
  • 27,179
  • 15
  • 70
  • 84
  • 12
    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. – PDixon724 Aug 15 '11 at 15:01
  • 2
    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 :( – ewall Aug 15 '11 at 15:10
  • 2
    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. – PDixon724 Aug 15 '11 at 17:39
  • 1
    Another 3rd-party tool that might be freely redistributable and easy to integrate and learn is [AutoIt](http://www.autoitscript.com/autoit3/); [this page](http://www.autoitscript.com/autoit3/docs/intro/autoit_on_vista.htm) demonstrates how the script requests elevated privileges. – ewall Aug 15 '11 at 18:08
  • 1
    Thanks ewall. It looks like I can redistribute AutoIt. I'll go down that road. Thanks again for the help! – PDixon724 Aug 16 '11 at 21:44
  • 5
    `psexec -h` doesn't work: 'Couldn't install PSEXESVC service: Access is denied.'. You need to already have the administrator rights to run psexec. – Nicolas Mar 14 '14 at 13:33
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ryan Bemrose
  • 9,018
  • 1
  • 41
  • 54
  • 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 forward – Nicolas Mommaerts Oct 10 '14 at 11:47
  • It seems that no matter what, the Start-Process removes all double quotes in the argumentlist.. – Nicolas Mommaerts Oct 10 '14 at 12:43
  • Works for me! I'm using it for `netsh int set int %wifiname% Enable/Disable`. – Marslo Oct 26 '16 at 12:52
  • 2
    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""""'"` – quakes Oct 26 '16 at 16:33
  • 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. – script'n'code May 15 '17 at 12:34
  • @quakes +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. – Bob Sammers Jun 26 '17 at 09:37
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.

Community
  • 1
  • 1
npocmaka
  • 55,367
  • 18
  • 148
  • 187
  • 1
    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. – RockPaperLz- Mask it or Casket Apr 11 '20 at 02:56
  • 4
    it's still in **medium integrity** not **elevated**, so you can't edit HKLM in `regedit`. It just skipped the uac. – ScriptKidd Apr 11 '20 at 03:11
  • `set "__COMPAT_LAYER=RunAsAdminitrator"` --- will enable UAC dialog; – Cylian Jul 05 '23 at 14:31
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

Gerardo Grignoli
  • 14,058
  • 7
  • 57
  • 68
10

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

Jamesfo
  • 524
  • 6
  • 11
  • Nicely done! Glad to see good answers to this question are still flowing in. – RockPaperLz- Mask it or Casket Feb 10 '21 at 07:48
  • 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. – My1 Apr 26 '21 at 12:47
  • 2
    I would suggest changing `... && exit /b)` to `... && popd && exit /b)`. that way the current directory keeps unchanged after running the script. – Luuk Feb 11 '22 at 12:41
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.
Pyprohly
  • 1,128
  • 1
  • 11
  • 10
  • 1
    this line is magical, can you explain the first section net sess>nul 2>&1? – yww325 Jul 06 '20 at 17:21
  • 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. – Abdulhameed May 05 '21 at 11:48
  • @Abdulhameed 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. – Pyprohly May 05 '21 at 16:40
  • @yww325 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`). – Michel de Ruiter Aug 16 '22 at 09:35
7

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.

Michel de Ruiter
  • 7,131
  • 5
  • 49
  • 74
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"
:--------------------------------------
TanisDLJ
  • 966
  • 1
  • 12
  • 16
  • 2
    I like the arg processing in your script. But **note** that `cacls` is deprecated in Windows 7 and newer windows versions. – Matt Oct 13 '15 at 08:15
  • [Fsutil dirty](http://stackoverflow.com/a/21295806/2932052) is even better – Wolf Feb 03 '17 at 12:59
  • 1
    Do you know that the line `pushd "%CD%"` saves the current working directory and changes to the current working directory? – Wolf Feb 08 '17 at 09:07
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.

Hugo Delsing
  • 13,803
  • 5
  • 45
  • 72
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"'"
Manuel Alves
  • 3,885
  • 2
  • 30
  • 24
  • 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"'"` – Riegardt Steyn Sep 03 '20 at 10:40
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.

anzz1
  • 91
  • 1
  • 4
  • 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 or chmod 6710 ) - 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 group then adopt admin's group on execution") - Linux also has extended attributes to allow more complex permissions. – Jay M May 03 '23 at 14:13
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>
0
%1 start "" mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe","/c pushd ""%~dp0"" && ""%~s0"" ::","","runas",1)(window.close)&&exit
scruel
  • 426
  • 6
  • 14
-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 ...
Vaibhav Jain
  • 1,939
  • 26
  • 36
  • 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. – Radon8472 Mar 14 '19 at 21:58
  • 1
    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. – Andrei Belogortseff Apr 21 '20 at 23:01