script - Problems elevating permissions while running a batch (CMD) file
2014-04
I’ve put together a batch (CMD) file that we use to check various things on select servers. What is posted below is almost everything (minus sensitive info). If launched with the proper command line, the batch file will proceed to check the day and time that it is launched. This way we can put it in the STARTUP folder on our servers and when we log in, it runs and if the user logs in on a business day between 06:45 AM – 07:30 AM, certain checks are done (i.e., external apps are launched, etc.). Selectively, the proper app is run based on environment variables like COMPUTERNAME, USERNAME (which has been removed in the code below), etc.
One of the processes that we need to run requires elevation of rights (IISRESET). To accomplish this within a batch file, I used the example shown by Matt (thank you) found at http://stackoverflow.com/questions/7044985/how-can-i-auto-elevate-my-batch-file-so-that-it-requests-from-uac-admin-rights. I didn’t want to post on that thread because this is a new topic. In the batch file I provided below, it works properly as is (no guarantees or warranties implied however). But if I remove the “comment” characters (the double colons) by changing:
:BEGIN
If %COMPUTERNAME%==SERVER1 explorer.exe "c:\queue"
:: If %COMPUTERNAME%==SERVER1 Goto CHECKPRIVILEGES
If %COMPUTERNAME%==SERVER2 explorer.exe "c:\queue"
:: If %COMPUTERNAME%==SERVER2 Goto CHECKPRIVILEGES
:COMMON
...(into)...
:BEGIN
If %COMPUTERNAME%==SERVER1 explorer.exe "c:\queue"
If %COMPUTERNAME%==SERVER1 Goto CHECKPRIVILEGES
If %COMPUTERNAME%==SERVER2 explorer.exe "c:\queue"
If %COMPUTERNAME%==SERVER2 Goto CHECKPRIVILEGES
:COMMON
…then the batch file doesn’t run properly. When I remove the comments from the two lines, the batch file no longer properly evaluates variables (whether run manually or as part of the STARTUP process). Even though the batch file states:
If %COMPUTERNAME%==SERVER1
If %COMPUTERNAME%==SERVER2
…the command associated with SERVER1 runs properly but SERVER2 runs the commands associated with both SERVER1 and SERVER2. Then, the batch file stops and never executes any of the commands in the COMMON section:
:COMMON
explorer.exe /e,
Start services.msc
…etc…
I've tested a number of scenarios and I know that I am overlooking something simple right in front of my face. Can anyone see what is wrong with the batch file below?
Thank you
-------------------------------------------------------------
Complete batch file:
@Echo Off
:: ***** If not started using "-Login" with the cmd line then we won't even check the day or time *****
:: ***** We won't even consider what day or time it is if we run this CMD file manually *****
Set LaunchString=%1%
If [%LaunchString%] equ [] Goto BEGIN Rem ***** No parameters given *****
Call :UPCASE LaunchString
If not %LaunchString% equ -LOGIN Goto BEGIN
:: ***** See if we're running on a normal business weekday *****
:: ***** That way we can put this in server startup to run automatically at login during certain times *****
For /F "tokens=1 delims= " %%A IN ('Date /t') DO @(Set DayName=%%A)
If %DayName:~0,3% equ Mon Goto CONTINUE
If %DayName:~0,3% equ Tue Goto CONTINUE
If %DayName:~0,3% equ Wed Goto CONTINUE
If %DayName:~0,3% equ Thu Goto CONTINUE
If %DayName:~0,3% equ Fri Goto CONTINUE
Goto FINISH Rem ***** Not a business day so exit *****
:CONTINUE
:: Check if the time is between 06:45 and 07:30 and if not then exit otherwise continue processing
Setlocal enableextensions enabledelayedexpansion
Set tm=%time%
Set hh=!tm:~0,2!
Set mm=!tm:~3,2!
If !hh! equ 6 ( :: Hour is 6 (i.e., 06:xx AM)
If not !mm! gtr 44 ( :: - Since hour is 6, are minutes greater than 44 (i.e., after 06:45)?
Goto FINISH
)
) else If !hh! equ 7 ( :: Hour is 7 (i.e., 07:xx AM)
If not !mm! lss 30 ( :: - Since hour is 7, are minutes less than 31 (i.e., before 07:30)?
Goto FINISH
)
) else Goto FINISH
Endlocal
:: If manually launched without command line argument then we start here (no day or time check)
:BEGIN
If %COMPUTERNAME%==SERVER1 explorer.exe "c:\queue"
:: If %COMPUTERNAME%==SERVER1 Goto CHECKPRIVILEGES
If %COMPUTERNAME%==SERVER2 explorer.exe "c:\queue"
:: If %COMPUTERNAME%==SERVER2 Goto CHECKPRIVILEGES
:COMMON
explorer.exe /e,
Start services.msc
:: ***** Check if test file exists *****
If Exist "c:\test.log" Start c:\programA.exe
If Exist "d:\test.log" Start c:\programB.exe
:FINISH
Exit /B
Goto:EOF
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Subroutine - Convert a variable VALUE to all UPPER CASE.
:UPCASE
For %%i IN ("a=A" "b=B" "c=C" "d=D" "e=E" "f=F" "g=G" "h=H" "i=I" "j=J" "k=K" "l=L" "m=M" "n=N" "o=O" "p=P" "q=Q" "r=R" "s=S" "t=T" "u=U" "v=V" "w=W" "x=X" "y=Y" "z=Z") DO Call Set "%1=%%%1:%%~i%%"
Goto :EOF
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Subroutine - Elevate permissions to run IISRESET
:CHECKPRIVILEGES
Net FILE 1>NUL 2>NUL
If '%errorlevel%' == '0' ( Goto gotPrivileges ) else ( Goto getPrivileges )
:GETPRIVILEGES
If '%1'=='ELEV' (shift & goto gotPrivileges)
Setlocal DisableDelayedExpansion
Set "batchPath=%~0"
Setlocal EnableDelayedExpansion
ECHO Set UAC = CreateObject^("Shell.Application"^) > "%temp%\OEgetPrivileges.vbs"
ECHO UAC.ShellExecute "!batchPath!", "ELEV", "", "runas", 1 >> "%temp%\OEgetPrivileges.vbs"
"%temp%\OEgetPrivileges.vbs"
:: Del "%temp%\OEgetPrivileges.vbs"
Exit /B
:GOTPRIVILEGES
Setlocal & pushd .
CMD /k iisreset
Goto :EOF
Thank you all very much for your input and advise. Yes, I normally follow standards in any of my code (either always using all lower case or always all UPPER case for labels). Thanks for grabbing that.
So I figured out what my problem was. First, what is happening in my batch file is that it tests whether elevation is needed and if so, it runs a script to obtain elevated access. I was so focused on the elevation part that I wasn't even thinking about the rest of the batch file (and how it evaluates via a command line variable). Because it evaluates using a command line variables, this was causing the rest of the batch file execution to fail. So I took the portion that elevates privileges and rewrote it. I've posted the updated code below so that anyone who may need it can use it also. The code below elevates (if needed) and runs IISRESET elevated. Just added/update as needed.
The "not returning" to the labels portion that I was speaking of in my original post, that was because I was using Goto and not Call. Guess I've been starting into the screen for too long...
Thank you again!!
:CHECKPRIVILEGES
Setlocal
Set PrivLaunchCmd=%temp%\Cmd2Run.CMD
ECHO "%SystemRoot%\System32\iisreset.exe" > "%PrivLaunchCmd%"
Net FILE 1>NUL 2>NUL
If '%errorlevel%' == '0' Goto GOTPRIVILEGES
:GETPRIVILEGES
ECHO Set UAC = CreateObject^("Shell.Application"^) > "%temp%\OEgetPrivileges.vbs"
ECHO UAC.ShellExecute "%PrivLaunchCmd%", "ELEV", "", "runas", 1 >> "%temp%\OEgetPrivileges.vbs"
Call "%temp%\OEgetPrivileges.vbs"
Del "%temp%\OEgetPrivileges.vbs"
Del "%PrivLaunchCmd%"
Endlocal
Goto :EOF
:GOTPRIVILEGES
Call "%PrivLaunchCmd%"
Del "%PrivLaunchCmd%"
Endlocal
Goto :EOF
I am making the menu items called Lock for Microsoft's BitLocker. I've got it all right up to one point: locking HDD partitions requires administrator privileges.
How do I use process paths or CMD or something similar to start an application elevated, with arguments?
You need the Elevation Powertoy for Vista. It also works on Windows 7. http://blogs.technet.com/b/deploymentguys/archive/2009/01/21/the-elevation-powertoys-and-windows-7.aspx
Use the RUNAS
command and put your application name and its arguments within double quotes.
RUNAS /user:machinename\administrator "app arg1 arg2 \"filename with spaces\""