Sunday, September 14, 2014

Enabling Wireless Connections Automatically (Scripting Windows Events)

If you're like me, you prefer your computers with front-loading reel-to-reel tapes and a massive control panel that includes lots of switches and incandescent lights. Unfortunately, this isn't very flexible in today's annoyingly mobile world, so you're forced to use a laptop computer with a docking station. Of course, you would like to have the system automatically disable the wireless adapter and prefer Ethernet when it's in the dock--but how do it? Well, on Windows 7, it's a lot more complicated than it should be, but now you have this article to help you get there.

Of course, this advice can be adapted to suit any problem where you'd like to run a PowerShell script when a Windows Event is logged.  I'm sure you'll find something even more creative to do.

Detecting When You're Connected

In order to detect when the system is connected to Ethernet and run a scheduled task, we'll need to find an event to trigger on. My system, a Lenovo ThinkPad X230, has an Intel 82579LM Ethernet adapter, which conveniently notifies me whenever the state of the network link changes in the Windows System event log:



When "e1cexpress" logs an Event ID 32, I know the Ethernet cable is connected and I can safely shut off the wireless adapter. When an Event ID 27 is logged, then the cable is disconnected and I should turn the wireless adapter back on. How nice.

Putting Together A PowerShell Script

Now that we have an event we can toggle on, we need to put together a script to turn the adapter on and off. The best way to do this is to put together a PowerShell script that we can call to adjust the adapter setting. There is a really helpful article here that covers how to do it using a program called devcon, using WMI, or using the NetAdapter module.

If you're on Windows 7, you'll have a hard time finding a copy of devcon that will actually let you toggle the adapter. There is evidence that the version included with Windows Server 2003 x64 will work on Windows 7 x64, but I didn't explore that any further. The NetAdapter module is only available in PowerShell 3.0 on Windows 8. So, that leaves us with WMI. With the help of the aforementioned article, I put together this simple PowerShell script:
param([string]$mode)
$wmi = Get-WmiObject -Class Win32_NetworkAdapter -filter "Name LIKE '%Centrino%'"

if($mode -eq "enable") {
echo "Enabling adapter"
$wmi.enable()
} elseif($mode -eq "disable") {
echo "Disabling adapter"
$wmi.disable()
} else {
echo "Usage: <command> enable/disable"
}

When this script is called with an "enable" or "disable" as an argument, it will do a search for network adapters that contain the name "Centrino", store a reference to it, and then either enable or disable the adapter. I'm searching for "Centrino" because the registered name of my wireless adapter is "Intel(R) Centrino(R) Ultimate-N 6300 AGN", yours may be different. If you run the command "Get-WmiObject -Class Win32_NetworkAdapter" you'll get a list of all the adapters on your system and can put together a query that meets your needs. Just make sure that you're returning only the one adapter that you want to toggle. You'll need to save your finished script into a text file with a .ps1 extension. I saved mine to C:\Windows\scripts\switch_wireless.ps1.

This almost certainly is not the best way to do it, but it works. I'm sure there is a PowerShell expert out there somewhere who has already written this on one line. If you've got a better way, please share it.


Wasting Time With Authenticode

PowerShell includes extra security for scripts. Which is to say that you won't be able to run them unless you do some extra work. If you want to take advantage of all the security Windows has to offer, then you'll need to sign your new script, trust yourself as a code publisher, and set the execution policy for your machine to "AllSigned". If you want to just get on with it, you can add the argument "-executionpolicy bypass" in the next step and ignore all of this.  In hindsight, that is probably what I should have done.

If you want to do it the "right way," you'll need to sign your script. If you're already equipped to do this, you probably don't need the help of this article. If you're not, you probably want to generate some self-signed certificates, install them, and sign the application. I used the helpful tutorial here to generate a code signing certificate, install it into the appropriate certificate stores, and sign my script. You'll need the makecert utility, which means you'll need to download and install either the Windows SDK or a copy of Visual Studio Express from the Microsoft site. If you're using Windows 8, that step can be skipped, and the whole process is greatly simplified by this helpful script, which isn't going to work on Windows 7, so don't try.

An important final step not mentioned by the signing tutorial is that you need to install your signing certificate as a trusted publisher on your system. You can do this by running the following commands:
$cert = Get-ChildItem Certificate::CurrentUser\My\<path to cert>
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store "TrustedPublisher","LocalMachine"
$store.Open("ReadWrite")
$store.Add($cert)
$store.Close()
Once you have done this, you should be able to run "gci cert:\LocalMachine\TrustedPublisher" and see your certificate installed.

Finally, set your execution policy by running "Set-ExecutionPolicy AllSigned" and pressing yes when prompted by the scary error message. Now, you should be able to run your freshly signed script securely.

Configuring the Scheduled Task

Now that you have created your PowerShell script, and hopefully wrestled Authenticode into submission, you can link your script to the adapter events to finally arrive at our desired behavior. Go back to your Windows logs, find the event that you want to trigger on, and select it.

On the Action Pane, under "Event", click "Attach Task To This Event..." Go through the wizard, and create an event for both the connected and disconnected events, referencing your PowerShell script as the executable.


Once you've done this, we'll need to open up Scheduled Tasks and make some adjustments before it works correctly. If you don't, you're just going to see a text box with your script pop up every time the adapter is connected or disconnected.  Find your "Disable" event, and double-click it to make changes:


  1. Set the user account for the task to "SYSTEM", and make sure it's set to "Run with highest privileges" and to run "Hidden".
  2. On the Actions tab, edit the action that was automatically generated by the wizard. The action should be set to "Start a program" and the program to run should be changed to just "powershell". In the arguments field, you should have something like "-noprofile -file C:\Windows\scripts\switch_wireless.ps1 disable" for the event that will disable the adapter, and a similar argument but with "enable" for your other event. If you ignored everything about Authenticode in the previous section, this is where you want to add a switch for "-executionpolicy bypass" to your arguments so that your script won't be blocked by security policy.
  3. On the Conditions tab, make sure that nothing is checked, like "Start the task only if the computer is on AC power".
When you're done, it should look something like this:


Rinse and repeat for the "Enable" event, making sure to pass the correct argument to your script.

Testing It Out

OK, now test it out. You should have been testing and experimenting along the way, but if you haven't now is the time. With both scheduled tasks set your adapter should toggle on and off when you connect/disconnect your system from Ethernet.  Pretty neat, and it frees up your wireless bandwidth so that all of your iDevices aren't impeded from downloading cat videos.

No comments:

Post a Comment