I need to make a batch script to some some tasks: write and modify some few configurations in %userprofile% (unprivileged user), and query some registry info which needs administrator rights. The thing is, if I right click the script and run it as administrator, all stuff will be run as the *administrator* user (whoever it is) instead of my currently logged in %username%, and thus all changes in %userprofile% will be for the administrator user instead of the current one. Also, I can't just "run as <particular_username>" because I need to use the script in different PCs, so I do need to use a variable. First easy obvious option would be just splitting it into 2 different scripts, one for user and one to be run as admin. But I'd really like to make it in one single script if possible. I have tried to imagine possible "solutions": run the script as admin and somehow get current logged in %username%, or run as regular user and somehow automatically ask for admin privileges once reaching the part which needs it. But I don't know how to do any of them. Could someone help please? Thanks very much in advance. @abbodi1406: Hope you can see this if you have chance (and time...)
That's sounds out of my league (don't be fooled with my avatar ) maybe can help @BAU @pf100 @hearywarlot
You could self-elevate the script on your own terms, passing the arguments you need like in this example: Code: @echo off :: grab current user's SID [string-safe in scripts unlike using the name] for /f "tokens=2" %%s in ('whoami /user /fo list') do set CU=%%s :: set arguments to pass userprofile and currentuser above and any other command line parameters to self-elevate via powershell set "args=set "__UP__=%USERPROFILE%" &set "__CU__=%CU%" &set ?=y&call "%~f0" %*" &call set "args=%%args:"=\""%%" ::AveYo: self-elevate passing args and preventing loop reg query HKU\S-1-5-20 >nul 2>nul || if "%?%" neq "y" (powershell -c "start cmd -ArgumentList '/c %args%' -verb runas" &exit) :: should be elevated here echo [CURRENT USER] %__UP__% wmic useraccount where sid='%__CU__%' get name,sid /format:table if not defined __CU__ echo .. %%__CU__%% is not available because you've right-click Run as administrator &echo. &echo. echo [RUN AS ADMIN] %USERPROFILE% wmic useraccount where sid='%CU%' get name,sid /format:table pause exit/b Or group actions based on required rights, and only elevate when necessary like in this example: Code: @echo off if defined __JUMP__ goto :%__JUMP__% :peasant echo doin' stuff not requiring admin rights whoami /user /fo list echo. rem pause echo asking for admin rights... :: set arguments to jump to :admin label and pass any other command line parameters to self-elevate via powershell set "args=set __JUMP__=admin &set ?=y&call "%~f0" %*" &call set "args=%%args:"=\""%%" ::AveYo: self-elevate passing args and preventing loop reg query HKU\S-1-5-20 >nul 2>nul || if "%?%" neq "y" (powershell -c "start cmd -ArgumentList '/c %args%' -verb runas" &exit) :: should be elevated here goto :admin exit/b :admin echo doin' stuff requiring admin rights whoami /user /fo list echo. pause exit/b
I do this on my scripts. Because it doesn't use powershell, works with windows vista and later operating systems Code: INSERT HERE THE UNPRIVILEGED USER CODE ( >nul fsutil dirty query %SYSTEMDRIVE% 2>&1 || ( :: Create VBS script echo Set UAC = CreateObject^("Shell.Application"^)>"%TEMP%\elevate.vbs" echo UAC.ShellExecute "%~f0", "%TEMP%\elevate.vbs", "", "runas", 1 >>"%TEMP%\elevate.vbs" if exist "%TEMP%\elevate.vbs" start /b /wait >nul cscript /nologo "%TEMP%\elevate.vbs" 2>&1 :: Delete elevation script if exist if exist "%TEMP%\elevate.vbs" >nul del /f "%TEMP%\elevate.vbs" 2>&1 exit /b ) ) INSERT HERE THE CODE THAT NEEDS ADMIN PERMISSIONS Maybe it helps you
Thanks very much everyone for your advise. Certainly, what I'm actually seeking is grouping actions based on required rights; but also that actions done by regular user remain regular user's (in terms of file ownerships, permissions, stamps, etc), and admin actions remain admin's. So with that in mind, I have this: Code: @echo off %windir%\system32\reg.exe query "HKU\S-1-5-19" > nul 2>&1 && ( REM assuming user right-clicked -> run as admin echo ERROR: you must not run as admin echo. echo Press any key to exit... pause > nul goto :eof ) REM doing stuff as non-privileged user [...] ( %windir%\system32\reg.exe query "HKU\S-1-5-19" > nul 2>&1 || ( REM Create VBS script echo Set UAC = CreateObject^("Shell.Application"^) > "%TEMP%\elevate.vbs" echo UAC.ShellExecute "%~f0", "%TEMP%\elevate.vbs", "", "runas", 1 >> "%TEMP%\elevate.vbs" if exist "%TEMP%\elevate.vbs" start /b /wait > nul cscript /nologo "%TEMP%\elevate.vbs" 2>&1 REM Delete elevation script if exist if exist "%TEMP%\elevate.vbs" > nul del /f "%TEMP%\elevate.vbs" 2>&1 exit /b ) ) REM doing stuff as privileged user [...] echo Process complete echo Press any key to finish... pause > nul Maybe I'm wrong with the idea, advise always welcome... I tested both options from both @BAU (his second one) and @TigTex. Both do the job indeed, albeit I noticed they work by opening a brand new CMD session for the admin user alone once permission has been granted. For what I'm seeking this has a downpoint: this new session runs whole script from beginning again, and since script tells not to run as admin, the job is naturally never completed. Opening a new CMD session window necessarily, is this the only way it works? Or could there be a workaround? Another option I thought about was just using labels for each case, regular or admin, and goto corresponding label according to case. This would imply running script twice indeed, though at least better than having 2 scripts...
You could pass a parameter. Change Code: echo UAC.ShellExecute "%~f0", "%TEMP%\elevate.vbs", "", "runas", 1 >> "%TEMP%\elevate.vbs" by Code: echo UAC.ShellExecute "%~f0", "%Params%", "", "runas", 1 >> "%TEMP%\elevate.vbs" where %Params% represents parameters you pass to the script. Test for the existence of the parameter at the begining of the script.
Sorry, er... I'm actually a novice in all this matter and what I have done until now was based on limited knowledge and a lot of internet searches. Could you perhaps elaborate a bit more please? Like putting all stuff meant to be run as admin somewhat inside a %params% variable, or something like that? Thanks. PS: by the way, would all what I'm seeking still be "orthodox" (stable for work most of times), or am I actually going too "hacky" already and I better run the script twice?
It does not happen on my example - script runs first part as user, and just second part as admin - that's what the jump stuff is there for. It's the core directive behind elevation - you must spawn another process somehow! No workaround - by design. Anyway, it seems there is a lack of proper underlying user detection that I'm going to solve right here: Code: @echo off REM IT SHOULD NOT MATTER IF THE USER HAS RIGHT-CLICKED RUN AS ADMIN THE SCRIPT OR NOT ::AveYo: self-elevate passing args and preventing loop set "args="%~f0" %*" & call set "args=%%args:"=\""%%" reg query HKU\S-1-5-19>nul||(if "%?%" neq "y" powershell -c "start cmd -ArgumentList '/c set ?=y&call %args%' -verb runas" &exit) REM NOW DEFINITELY RUNNING AS ADMIN - EITHER WAY ::AveYo: get Domain\User irrespective of script being run as Admin - another of my neat tricks :) for /f tokens^=^13^ delims^=^" %%u in ('tasklist /fi "imagename eq explorer.exe" /fo csv /v /nh') do if "%%u" neq "," set "DU=%%u" echo Domain\User: %DU% ::AveYo: simple replacement to get just the User call set "CU=%%DU:%COMPUTERNAME%\=%%" echo User: %CU% ::AveYo: get SID for User - can be input in my reg_takeownership snippet or icacls instead of domain\name for /f "tokens=1" %%s in ('wmic useraccount where ^(name^="%CU%" and domain^="%COMPUTERNAME%"^) get sid /value') do set "%%s">nul echo SID: %SID% ::AveYo: and the Domain\User running as admin being: echo Domain\User (Elevated): %COMPUTERNAME%\%USERNAME% echo. ::AveYo: now let's put it to a test with a real-life example takeown /f "C:\Program Files\Windows NT\Accessories\*" /r /skipsl /a icacls "C:\Program Files\Windows NT\Accessories\*" /grant:r "%DU%":F pause exit So with this method at your disposal, it no longer matters at which point you decide to elevate the script yourself, as you can determine the underlying user if the script was right-clicked - run as admin. The most convenient is at the beginning, but you can also group actions by rights (properly this time) as in my 2nd example, and then ask for elevation just for the admin part (and can make that window hidden - just google powershell start-process)
@BAU: Thanks yet again for all your advise. I'll double-check the options again. Finally, and just curious, do you think it could be possible to do without powershell, i.e., only with cmd stuff? Thanks again.
there are not that many other ways to elevate (mshta/rundll32/jscript/vbscript/schtasks/wmic) and most have uglier syntax than the powershell one-liner, and the show-stopper is that anti-virus software including built-in defender flag those other alternatives as malicious. use powershell - it's more convenient and more compatible (comes with windows since 7) and all chances are it wont be blocked by some lame av or sys admin even before reaching your users
I believe powershell is the way to go. With vbscript you may encounter issues when directory names include special or foreign characters. @BAU : great scripting!
Hi, I think you can use this script https://forums.mydigitallife.net/th...ts-and-special-chars-incl-quote-no-tmp.74332/ If you embed the VBS part using the "elevate_no-arguments" code and embed it as described, you can run the script without ever making any temporary files (write is a bottleneck if you think about it) and the thing looks Waaaaaay cleaner. If you use the normal VBS elevate code, you can even use some special characters that normally cause VBS to stop working, like quotes and others. Ofcourse Powershell is more functional, but unless a very sensitive AV or system administrator blocks VBS, CMD or embedded scripts, in 80% of the other cases this should run nearly everywhere, should work even on XP. You could use the script like this: Code: <!-- : Begin batch script @ECHO OFF FSUTIL dirty query "%SYSTEMDRIVE%" >NUL && ( call :AdminPart ) || ( ECHO(************************************* ECHO(Invoking UAC for Privilege Escalation ECHO(************************************* CSCRIPT //nologo "%~f0?.wsf" //job:ELAV /File:"%~f0" call :UserPart ) echo(=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= echo( This code is run as either Admin or User echo( I'll just add a line to make things look neat echo( echo(Below are all the environment variables echo( set echo(=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= pause exit /b :AdminPart echo( Only runs as Admin and stuff echo( I run as %USERNAME% and my userprofile is %userprofile% exit /b :UserPart echo( Only runs as current user and stuff echo( I run as %USERNAME% and my userprofile is %userprofile% exit /b ----- Begin wsf script ---> <package> <job id="ELAV"> <script language="VBScript"> Set strArg = WScript.Arguments.Named If Not strArg.Exists("File") Then Wscript.Echo "Switch /File:<File> is missing." WScript.Quit 1 End If CreateObject("Shell.Application").ShellExecute strArg("File"), ELAV, "", "runas", 1 </script> </job> </package>
Sorry for bringing this back... I'm already taking your useful advises. But one thing came to mind: when elevating to privileged user, what if not successfully elevated? I mean, what if for some reason admin's password is not successfully entered? Commonly because forgotten password and need time to remember, but having to hit Cancel and stopping the script. Is there a way in CMD (not powershell) to display messages or do stuff in case admin password is not successfully entered?
Get Power Run from Sordum, drag a .reg .bat .cmd to its UI and double click. Everything gets elevated and its never failed yet. Sometimes those files run so fast somethings get skipped, add a 1 second timeout between commands, its slower and hasnt failed yet . "hurry burry spoils the curry".
Let me try again. Is there any way to validate whether admin password was successfully entered or not?