Contact

Subscribe via Email

Subscribe via RSS

Categories

Recent Posts

Creative Commons Attribution 4.0 International License
© Rakhesh Sasidharan

Find which bay an HP blade server is in

So here’s the situation. We have a bunch of HP rack enclosures. Some blade servers were moved from one rack to another but the person doing the move forgot to note down the new location. I knew the iLO IP address but didn’t know which enclosure it was in. Rather than login to each enclosure OA, expand the device bays and iLO info and find the blade I was interested in, I wrote this batch file that makes use of SSH/ PLINK to quickly find the enclosure the blade was in.

Put this in a batch file in the same folder as PLINK and run it.

Note that this does depend on SSH access being allowed to your enclosures.

Update: An alternative way if you want to use PowerShell for the looping –

 

Batch file to add BUILTIN\Administrators group to a folder of roaming profiles

Based on the idea I elaborated in my previous post

Latest version can be found on GitHub

I will explain the Batch file in detail later.

Enabling disabled Office add-ins automatically

So as I mentioned yesterday I have been thinking of using Task Scheduler to try and enable disabled Office add-ins automatically.

The Problem

Here’s what happens. Every so often when users open Word, Outlook, Excel, etc they get a prompt like the one below:

addin-error

We don’t know why they keep getting it, but ideally users should be clicking “NO” as we use all these add-ins and don’t want them disabled. But users being users they click “YES” intentionally or inadvertently and then call IT with complaints that their Word, Outlook, etc aren’t working as expected. Then we in IT have to do the boring task of going to their add-ins screen and enabling these items; followed by closing and opening Word, Outlook, whatever.

What I want to do here is (1) warn users when the above dialog box comes up, advising them not to click the “YES” button; and (2) if they do click “YES” then somehow enable the add-ins behind their back and get them to close and open the affected program.

Add-ins and the registry

I started off thinking I might need to do some fancy stuff to enable add-ins, but soon realized that they are all controlled by the registry. Nice!

  • Add-ins can be installed for all users on the machine or just a specific user. So you have to look under both HKLM and HKCU. The two locations are: addin-registryHKCU\Software\Microsoft\Office\(application)\Addins\ and HKLM\Software\Microsoft\Office\(application)\Addins\ (replace (application) with Word, Outlook, Excel, or PowerPoint). These two locations contain keys for each add-in installed for that particular application.Here’s a screen shot for the Outlook InterAction add-in for a user.
  • The key thing in the registry keys above is the LoadBehavior entry. This can take many values – the default is a value of 3 which means the add-in is loaded automatically at startup and is currently loaded. If the value is 2 it means the add-in is loaded automatically at startup but is currently not loaded.
  • My initial hunch was that LoadBehavior is what I must target. I thought when a user disables an add-in the LoadBehavior value of that add-in probably changes to 2, so all I need to do is switch it back to 3. But I was wrong (as I quickly figured after crashing Word a couple of times and choosing to disable add-ins).
  • Then I discovered that add-ins which are disabled via a user dialog box as above don’t have their LoadBehavior value changed; rather they are set as disabled at another place in the registry. And that is under the HKCU hive, at HKCU\Software\Microsoft\Office\(version)\(application)\Resiliency\DisabledItems (replace (version) with 15.0 for Office 2013, 14.0 for Office 2010, 12.0 for Office 2007, 11.0 for Office 2003, you get the idea … and replace (application) with Word, Outlook, etc as before).

Each time an add-in is disabled, a REG_BINARY entry is created under HKCU\Software\Microsoft\Office\(version)\(application)\Resiliency\DisabledItems with a binary value. I didn’t experiment to see if there’s a one-to-one mapping between this entry and an add-in, and also whether the entry is same for an add-in across all machines; the important finding for me was that if I delete this entry, it enables the add-in. So all I really needed to do to see if there are disabled add-ins for a program is to just check this key, and if there are any entries I can delete them all to re-enable all the add-ins. Nice! This doesn’t require a PowerShell script really. Good old reg can do the trick too. So a batch file is more than sufficient.

Below is what I came up with:

I could probably make it shorter if I pick up more batch file scripting but this does the trick for me.

Let’s break it down for Outlook. I enumerate the registry key. The results of this are iterated over by a FOR loop. If the list isn’t empty – i.e. there are disabled add-ins – the loop exits and goes to a different section.

This section that the loop exits to simply deletes all entries under that key, shows a message to the user asking them to close and open Outlook, and goes up the batch file to check Word next.

Repeat and rinse for each application and there you have it! Like I said, easy peasy, quick and dirty!

Maybe I should improve the script later – I don’t know. An overhaul using PowerShell might be a good idea I think. Specifically, there are a few things I am already thinking of adding:

  1. Currently the message box goes away if the user clicks OK. Would be nice if I could keep an eye out for whether they actually close the program later, and if not keep popping up reminders – maybe a ballon tip?
  2. Would be nice if I could close the application for the user. I could do that in the batch file too via taskkill but I was wary of doing that lest it cause any corruptions. I wouldn’t want Outlook OST and PST files or Word templates getting corrupted because I closed the program. Maybe PowerShell has a more graceful way of shutting down an app? I don’t think so, but that could be my ignorance.
  3. Currently I enable all add-ins. While that’s fine for our case maybe I should have a list of add-ins that are to be excluded from being enabled? Or only enable a whitelist of add-ins?

So that’s that. Next step is to find a way to trigger the batch file.

Scheduled task triggers and Event Viewer

Each time the dialog box asking a user to disable an add-in appears, an entry is logged in Event Viewer. This looked like a promising way to hook on to that event and warn the user not to click “YES”. These entries are logged under “Application and Services Logs” > “Microsoft Office Alerts”.

addin-ev-alert

The joy of discovering that was short lived though, as I realized the same sort of alerts are generated for many other Office related things: spell check notifications, connectivity to Exchange, etc.

misc-ev-alert

All these alerts had the same source and ID and there was nothing for me to distinguish between them! Sure the data part was different, and while Event Viewer (and thus Task Scheduler) supports XPath for searching the Event Logs it has limitations in that I can’t do wildcard searches. Thus while I could do an XPath search for all events that have the exact error message in the first Event Log entry above, I can’t do an XPath search for all events that contain the text “Do you want to disable this add-in?”. This is a big limitation because now I will have to (a) find the exact message for each add-in and program, and (b) create XPath queries for each of these – eugh! That’s too much work for now (and not very elegant either!) so I decided to not pursue that route.

Once a user decides to enable or disable an add-in though, I found a different alert is generated for that. Under the “Application” log, from source “Microsoft Office 14” (since we have Office 2010), alerts with event ID 2001 are generated each time a user selects to disable an add-in (and alerts with event ID 2000 are generated when they choose not to disable).

2001-alert

That’s convenient, coz now I can create a task that is triggered on such events and whose action is to run the previous batch file. Nice!

Putting it all together

End result is a task I can import as in the case of the laptop lid issue. Here’s the XML file of the task:

Once again, I install it for the users I want via a batch file like this:

Not done yet!

I am not done yet though. It’s not practical installing it like this for each user in my domain. I chose the above approach just to get it installed for a few test users; next step is to roll it out via GPO to the whole domain. Stay tuned for a post on that later …!

And one more thing …

Something I learnt while reading about add-ins.

Windows Task Scheduler quirks; Changing what happens when a laptop lid is closed … (part 3)

The other day I posted on how I use Task Scheduler to run a batch file that changes what happens to a laptop when the lid is closed depending on where it is located.

I have been fiddling with the Task Scheduler since then and thinking of how I can use it for other tasks. Specifically, at work we have a lot of add-in issues with Office products wherein every so often Word/ Excel/ Outlook/ PowerPoint give a message when launching that such and such add-in caused issues and it is recommended the user disables them; users click “Yes” to such messages inadvertently or intentionally, after which whatever functionality the add-in provides does not work any more and we at IT get calls! I had an idea to create a PowerShell script that will re-enable the add-in in such cases and maybe use Task Scheduler to hook into any Event Log entries that are generated when add-ins are disabled to trigger the running of this script. That’s the topic of another post however but as a result of exploring this I have been playing with Task Scheduler a bit and revisited the Task Scheduler entry I made for the laptop lid closing problem.

First off, here’s an exported version of the Task Scheduler entry I created:

Note the highlighted lines:

  • Line 5 – the “Author” – doesn’t matter much. It merely specifies who the author of the Task is.
  • Line 23 – the “UserID” – matters. This is the user ID under which the task runs. If you specify a user ID here, you must use the same ID when importing the task. You could specify a user ID here and specify a different ID when importing (via schtasks /ru ...) and the latter will over-ride the ID in the XML file. If you will always be specifying a user ID when importing, you can skip the user ID in the XML file too. I chose to leave it commented so I remember it for later.
  • Line 17 – yet another “UserID” – matters. This user ID is to do with the “run when a user logs in” trigger. If your task doesn’t have such a trigger this user ID won’t be present. You have two choices here really: either you could specify a user ID and that is the user account whose login will trigger the task, or you could skip that tag (remove the whole line) and the task will trigger when any user account logs in. The latter has a catch though. If you do this, then you cannot skip the UserID of line 23. In my case I chose to use a marker here “–REPLACE–” and modify the XML file to replace this with a user ID later on (see below).

Gist of the matter for my specific scheduled task entry is that I can’t import this task willy for any user. When importing I have to (1) specify the user in whose context the task will run – I must do that via schtasks /ru ... or modify the XML file directly – and (2) if I am not modifying the XML file in the previous step, I can’t leave the UserID element of line 17 blank and so I must modify the XML file to put a user ID here. This user ID would be that of the user I am installing it for of course.

To that end I modified the install batch file I had created:

The important bit here is the 4th line, where I use PowerShell to read the XML file and replace the bit that says “–REPLACE–” (which is the marker I use at line 17 of the XML file) with the user ID of the user I am installing this task for. Apart from that, on line 6 when I import the task I get the user ID from the environment variables and prompt for a password. This way the install batch file and task XML files are generic and I can run them for any user.

That’s all for now!

Update: I realized I could simplify things further since I am using PowerShell any ways to replace text in the task scheduler XML file. I can specify the “run as” user too and thus avoid prompting for a password when installing! :)

Notice I have set the marker “–REPLACE–” in two places now. Both will get replaced while installing. The batch file can be modified as below to omit the /RU & /RP switches:

Changing what happens when a laptop lid is closed … (part 2)

Right, so yesterday I posted about this batch file I created to set a laptop to sleep or do nothing depending on whether it’s out of office or in office (which the batch file determines by pinging servers in each network).

What I want to do now is automate the running of this batch file. I don’t want to have the user clicking on this file. Since the file only needs to run each time a change in the network state happens ideally something should just trigger it to run when the state changes.

Use Task Scheduler

I know the Windows Task Scheduler is quite powerful and can trigger tasks on Event Log entries. So if I can find the Event Log entries generated when I connect and disconnect the network, am good to go! I checked the Event Logs on the user laptop and sure enough there were entries. For the wired port they seemed to be tied to the card driver and generated by the driver, but for the wireless port they were generated by Windows itself. Here’s what I found:

  • Event ID 33 is generated in the System log from source “e1dexpress” whenever the LAN port is connected to a network.
  • Event ID 27 is generated in the System log from source “e1dexpress” whenever the LAN port is disconnected to a network.
  • Event ID 8001 is generated in the Applications and Services > Microsoft > Windows > WLAN-AutoConfig > Operational log from source “WLAN-AutoConfig” whenever the WiFi is connected to a network.
  • Event ID 8003 is generated in the Applications and Services > Microsoft > Windows > WLAN-AutoConfig > Operational log from source “WLAN-AutoConfig” whenever the WiFi is disconnected from a network.

Based on this I can create a new scheduled task entry to run my batch file and add triggers for the Event Log entries above. Here’s an example:

image002

And here’s the final list with all the triggers:

image001

Notice I added a trigger to run when the user logs in too. Added this for situations where the user may dock the laptop while it’s powered off and then login. In such I wasn’t sure whether the network connect event log would be generated or not and whether it would be generated before the Task Scheduler runs – was too lazy to test really, so I added an entry to run when the user logs in.

That’s it really! Once such a task is created it runs the batch file as expected.

I did two additional things after creating the task:

  1. By default tasks are set to run only if the computer is on AC power. Since we want it to run when on battery too, be sure to untick that.
  2. Right click the task and export it. This way I can easily import it on other similar laptops. For dis-similar laptops (where the network card model may be different) I’ll have to import and add additional triggers. Still, better than recreating the task from scratch, and I think what I’ll do is add triggers for such models too to the task I created so eventually I’ll have a task that can handle all models in our firm. This way I can import it to any machine without worrying about the model differences.

Importing task on a remote machine

Here’s what I for users who want this on their laptop: I copy the batch file and list of IPs to a folder on the user machine; then I launch Task Scheduler on my machine, connect to the Task Scheduler on user machine, import the task I made above. I don’t even have to visit the user machine, how cool is that!

Importing task on a local machine

If I am at a user machine and want to set them up with the batch file, I don’t want to bother with copying the files and importing a task. I want to make that an easy step too so I created a batch file that copies over the two files and imports the task and runs it once! Just double click the install batch file in such cases and it will do the rest.

Quite obvious what’s happening. The trick here is to use the schtasks command which is a command line interface to manage Scheduled Tasks. Using schtasks I can import the task and run it.

I can use schtasks with a remote computer too so one of these days I’ll probably create another batch file to handle remote additions too.

That’s all for now!

Update: See this post for an updated version of the batch file as well as the XML file I use to import the task.

Changing what happens when a laptop lid is closed depending on its location

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:

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.

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:

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:

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:

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):

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.

Find removable drive letter from label

Been a while since I posted here, and while the following is something trivial it is nevertheless something I had to Google a bit to find out, so here goes.

I have a lot of micro SD cards to which I sync my music. The cards are labelled “Card1”, “Card2”, and so on. Corresponding to these I have folders called “Card1”, “Card2”, and so on. I have a batch file that runs robocopy to copy from the folder to card. I have to specify the drive letter of the card to the batch file, but now I am being lazy and want to just double click the batch file and get it to figure out the drive letter. After all the drive with a label “CardX” will be the one I want to use.

How can I get the drive letter from the label? There are many ways probably, since I like WMIC here’s how I will do it:

This command will list all the volumes on the computer. There’s a lot of output, so a better way is to use the /format:list switch to get a listed output:

Here’s an example output for my micro SD card:

It’s easy to see that the Label property can be used to check the label. The DriveLetter property gives me the drive name. If I want to target removable disks only (as is my case) the DriveType property can be used.

Thus for instance, to filter by the label I can do:

Or to filter by both label and type I can do:

To output only the drive letter, I can use the get verb of WMIC:

The second command is what I will use further as it returns the output in a way I know how to use.

In batch files the FOR loop can be used to parse output and do things with it. The best place to get start with is by typing help for in a command prompt to get the help text. Here’s the bit that caught my attention:

Finally, you can use the FOR /F command to parse the output of a
command. You do this by making the file-set between the
parenthesis a back quoted string. It will be treated as a command
line, which is passed to a child CMD.EXE and the output is captured
into memory and parsed as if it was a file. So the following
example:

FOR /F “usebackq delims==” %i IN (set) DO @echo %i

would enumerate the environment variable names in the current
environment.

This looks like what I want. The usebackq option tells FOR that the text between the back-ticks – set in the example above – is to be treated like a command and the output of that is to be parsed. The delims== option tells FOR that the delimiter between parts of the output is the = character. In case of the example above, since the output of set is a series of text of the form VARIABLE=VALUE, this text will be split along the = character and the %i variable will be assigned the part on the left. So the snippet above will output a list of VARIABLEs.

If I adapt that command to my command above, I have the following:

Output from the above code looks like this:

As expected, the picks the DriveLetter part. What I want instead is the bit on the right hand side, so I use the tokens option for that. I want the second token, so I use tokens=2. I learnt about this from the FOR help page.

Here’s the final command:

That’s it! Now to assign this to a variable I do change the echo to a variable assignment:

Here’s the final result in a batch file: