The other day a user complained that whenever he’d shut the laptop lid it would go to sleep. I checked the power settings in Windows 7 and sure enough it was set that way. So I changed it to not sleep, but the user called back the next day to say it slept off again! This looked like a policy setting so I ran gpresult
. to get the policies applied to the computer:
1 |
C:\> gpresult /scope Computer /h gpresult.html |
Sure enough there was a policy that set the laptop to sleep when its lid was closed.
I checked with our Desktops team to change this but apparently this was intentional. For security reasons the firm didn’t want users closing their laptop lid when out of office – and the laptop stays powered on – with the risk that it could get stolen and its data can be accessed. If the laptop were to sleep at least the data is encrypted and cannot be accessed unless a user signs in. That made sense so what I needed was a way to set the laptop to sleep only if it’s in the office.
Step 1: Create a batch file
My immediate idea was to give the user a batch file he can double click on when in the office so it changes the laptop setting to not sleep. I knew that the powercfg
command can do this so all I had to do was create a batch file with the two commands and leave it on the user desktop. When in office and the user wants to close his laptop lid, double click the file so the setting is changed. After a while GPOs will refresh and reset the setting anyways. Not great, but a quick and dirty fix to get things going.
The link I refer to earlier is for Windows Vista and it didn’t work on Windows 7. Not an issue, found one that does work.
1 2 |
powercfg -setacvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e 4f971e89-eebd-4455-a8de-9e59040e7347 5ca83367-6e45-459f-a27b-476b1d01c936 0 powercfg -setdcvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e 4f971e89-eebd-4455-a8de-9e59040e7347 5ca83367-6e45-459f-a27b-476b1d01c936 0 |
To avoid the user running the batch file when out of the office, I added a bit to ping one of our DCs first before running the powercfg
commands. ;-)
Final batch file was like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@ECHO OFF ping -n 1 10.10.10.10 IF ERRORLEVEL 1 GOTO NOTLAN ECHO Machine is in office. Closing lid will not put laptop to sleep. powercfg -setacvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e 4f971e89-eebd-4455-a8de-9e59040e7347 5ca83367-6e45-459f-a27b-476b1d01c936 0 powercfg -setdcvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e 4f971e89-eebd-4455-a8de-9e59040e7347 5ca83367-6e45-459f-a27b-476b1d01c936 0 GOTO EXIT :NOTLAN ECHO Machine is not in office. Closing lid will put laptop to sleep. powercfg -setacvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e 4f971e89-eebd-4455-a8de-9e59040e7347 5ca83367-6e45-459f-a27b-476b1d01c936 1 powercfg -setdcvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e 4f971e89-eebd-4455-a8de-9e59040e7347 5ca83367-6e45-459f-a27b-476b1d01c936 1 GOTO EXIT :EXIT |
Easy peasy!
Step 2: Modify batch file for WiFi too
Then I thought why limit myself to the office LAN. What about when the user is in one of our meeting rooms for instance? The ping to DC will fail but we have office-wide WiFi so I can ping that instead. Thus the batch file was modified:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
@ECHO OFF ping -n 1 10.10.10.10 IF ERRORLEVEL 1 GOTO NOTLAN ECHO Machine is in office. Closing lid will not put laptop to sleep. powercfg -setacvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e 4f971e89-eebd-4455-a8de-9e59040e7347 5ca83367-6e45-459f-a27b-476b1d01c936 0 powercfg -setdcvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e 4f971e89-eebd-4455-a8de-9e59040e7347 5ca83367-6e45-459f-a27b-476b1d01c936 0 GOTO EXIT :NOTLAN ping -n 1 192.168.1.1 IF ERRORLEVEL 1 GOTO NOTWIFIMUS ECHO Machine is in office. Closing lid will not put laptop to sleep. powercfg -setacvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e 4f971e89-eebd-4455-a8de-9e59040e7347 5ca83367-6e45-459f-a27b-476b1d01c936 0 powercfg -setdcvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e 4f971e89-eebd-4455-a8de-9e59040e7347 5ca83367-6e45-459f-a27b-476b1d01c936 0 GOTO EXIT :NOTWIFIMUS msg * "Machine is not in office. Closing lid will put laptop to sleep". powercfg -setacvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e 4f971e89-eebd-4455-a8de-9e59040e7347 5ca83367-6e45-459f-a27b-476b1d01c936 1 powercfg -setdcvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e 4f971e89-eebd-4455-a8de-9e59040e7347 5ca83367-6e45-459f-a27b-476b1d01c936 1 GOTO EXIT :EXIT |
Step 3: Make batch file more generic
Nice! Ok, what can I do to target the laptop being in one of our offices in another country? They too have office WiFi but a different address. Yes I could create more GOTO
sections like above but that’s not very neat is it? Moreover I don’t want users or my colleagues in IT to have to modify the batch file each time they want to add an IP address. The code and data should be independent. Less chances of errors too when modifications are made!
Thus was born the idea of putting all these IP addresses in a file and using a FOR
loop to go through them. Wish I could have put the IP addresses in the same batch file but I couldn’t find a way of making a FOR
loop iterate over elements in a variable. Anyhoo, not a big deal – a separate file is better too as there’s zero chance of anyone changing the code by mistake.
I wish my DOS Batch file skills were great, but they are not. I know the basics – like I knew the FOR
loop could do what I want – but it’s been ages since I used batch files for any complicated stuff. Not a problem, did a bit of searching and I found an example I could modify. The important bit for me was to break out of the FOR
loop once I find an office that match. For that I needed an IF
clause.
While I was at it I also removed the ECHO
line – which the user will never see as the batch file runs and closes anyway! – with a msg
line. This command is present on Windows XP onwards so all our laptops have it by default. Here’s the net result:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
@ECHO OFF setlocal enableextensions enabledelayedexpansion FOR /F "delims=," %%i in (C:\officepoints.txt) do ( SET bHOSTUP=0 ping -n 1 %%i | find "TTL=" > NUL && SET bHOSTUP=1 IF !bHOSTUP! equ 1 GOTO INOFFICE ) GOTO OUTOFFICE :INOFFICE msg * /time:2 "Machine is in office. Closing lid will not put laptop to sleep". REM powercfg -setacvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e 4f971e89-eebd-4455-a8de-9e59040e7347 5ca83367-6e45-459f-a27b-476b1d01c936 0 REM powercfg -setdcvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e 4f971e89-eebd-4455-a8de-9e59040e7347 5ca83367-6e45-459f-a27b-476b1d01c936 0 GOTO EOF :OUTOFFICE msg /time:2 * "Machine is not in office. Closing lid will put laptop to sleep". REM powercfg -setacvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e 4f971e89-eebd-4455-a8de-9e59040e7347 5ca83367-6e45-459f-a27b-476b1d01c936 1 REM powercfg -setdcvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e 4f971e89-eebd-4455-a8de-9e59040e7347 5ca83367-6e45-459f-a27b-476b1d01c936 1 GOTO EOF :EOF exit /B |
Here’s the breakdown of what’s happening:
First, I create a file called C:\officepoints.txt
. That file has IP addresses each on a line (or put them all in a line comma separated – that too will work):
1 2 |
10.10.10.10 192.168.1.1 |
The above file is read by the FOR
loop. If the addresses are comma-separated that’s ok. Else it expects them on a line each. Read the address, ping it with one packet, check whether the result has the word “TTL” in it, and if yes set a variable bHOSTUP
to 1. Unlike earlier I am now searching whether the ping output has the word “TTL” in it because I realized that when pinging the office WiFi the ping command would return with a non-error ERRORLEVEL
(i.e. ERRORLEVEL
is 0 and not 1 even though it can’t ping) because it thinks the destination is unreachable from the router rather than being unreachable from the laptop – so as far as ping is concerned it isn’t a laptop issue and so there’s no error. Not sure why it does that. Searching for the word “TTL” means such problems are overcome because all successful pings will contain the word “TTL” in them.
And that’s it really. Depending on whether it gets a ping for one of the IP addresses the FOR
loop is existed and the appropriate powercfg
command runs. If the loop is never exited thus, once it exits normally a powercfg
command is run to ensure the laptop sleeps when the lid is closed.
Step 4: Automating it
This is all fine and dandy, so how about automating it? Rather than have the user double click a batch file can I make this happen automatically whenever he connects and disconnects? Yup … time is running out now so I will go into that in my next post! Stay tuned.