FOG Snapin to Deploy Ruckus Pre-Shared Keys (PSKs) to Windows 7 Laptops

The Problem...

At one of our clients' sites where we installed FOG (An open-source, networked computer hard drive imaging solution), there was only one thing preventing them from reaching the goal of having practically hands-free deployments of their Windows 7 images to their laptops: The wireless profile configuration.

We had already created a FOG snapin to delpoy Microsoft Office, and another one to activate the Microsoft Office installation, but after a deployment of their Windows 7 images to a batch of laptops, an administrator would need to log into each laptop and then manually connect them to their wireless network. This is a time-consuming process and is exactly the kind of thing that an imaging solution such as FOG is meant to alleviate.

Creating this wireless configuration snapin would be much easier if every laptop connected to the wireless network with the same WPA/WPA2 passphrase, but then the administrators would loose the flexibility of the dynamic pre-shared keys (PSKs) feature that their Ruckus ZoneDirector wireless controller offers.

Dynamic PSKs are a convenient Ruckus feature which allows an administrator to assign unique WPA/WPA2 wireless passphrases to devices. This is especially useful in BYOD-friendly networks, or on networks where a RADIUS server is not available, and it makes managing a wireless network and connecting devices a breeze.

However, having individual, unique PSKs per device is the reason that visiting each laptop and creating the wireless configuration manually is required after deploying the image.

So the problem becomes; "How do you create a generic FOG snapin to deploy individualized settings for each laptop?"


The Solution...

A combination of Samba, Windows Powershell, and Bash shell scripting "magic." (OK, not really magic... :)

Right away, we determined that there were two hurdles to overcome:

  1. Can Windows 7 be made to join a wireless network with DOS, Windows Powershell, or native commands and/or scripts?
  2. How do we correlate a PSK with a specific laptop to which it is assigned, and create a wlan profile using said commands or scripts?



Creating a Windows 7 wlan Profile from the Command Line
After spending a good deal of time searching for a method to create wlan profile using Powershell, I was convinced that I would have to find another way. The methods I found in some online how-tos were, in my opinion, far too complicated to accomplish what I considered to be a simple task.

In my continued searches I found that the Windows netsh.exe command had the ability to display, delete, and add wlan profiles - among about a million other functions.

The command seemed simple enough to me:

netsh wlan add Profile filename="wlanProfileName.XML"

I found the answer to my question, and I was excited until I learned that the netsh command required an XML file as an input to its filename=" command line parameter. However, with a little more searching I was able to locate the format required.

Below is what the XML looks like for a Windows 7 netsh command's wlan profile input file. The parts in bold are the parts that are important for our FOG snapin:

<?xml version="1.0" encoding="US-ASCII"?>
<WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1">
  <name>WLAN Profile Name</name>
  <SSIDConfig>
    <SSID>
      <name>SSID</name>
    </SSID>
  </SSIDConfig>
  <connectionType>ESS</connectionType>
  <connectionMode>auto</connectionMode>
  <autoSwitch>false</autoSwitch>
  <MSM>
    <security>
      <authEncryption>
        <authentication>WPA2PSK</authentication>
        <encryption>AES</encryption>
        <useOneX>false</useOneX>
      </authEncryption>
      <sharedKey>
        <keyType>passPhrase</keyType>
        <protected>false</protected>
        <keyMaterial>PreSharedKey</keyMaterial>
      </sharedKey>
    </security>
  </MSM>
</WLANProfile>

On a Windows 7 laptop, I created an XML file and set the "SSID" and "PreSharedKey" entries (listed in bold above) to the SSID and passphrase matching one of our nearby wireless networks, and set the "WLAN Profile Name" to something sensible like "SchoolName Wireless Profile". This field is just the name of the wireless profile in the list of stored wireless profiles, and is not important. Of course "SchoolName" was replaced with the actual name of the school. :)

I issued the netsh command and instantly, a new wireless profile appeared in Wireless Networks dialog box on the Windows 7 laptop, and the laptop connected to the wireless network. Success!

In my mind, the biggest hurdle was conquered. I started out not knowing if Windows 7 could be made to join a wireless network from the command line, and now I had a (kind of) simple one-line command to do it. I knew that I could easily write a FOG snapin that could run this command using a Powershell script or a DOS batch file, but I still needed to figure out a way to generate the XML file, and then how to associate the correct PSK with the workstation running the snapin script.



First Idea
The first idea for creating this FOG snapin was to write a single Windows Powershell script which, when deployed and run by the FOG service on the laptops, would perform the following actions:

  1. Determine the name of the workstation that the script is running on
  2. Read a CSV file on a hidden, read-only Samba fileshare
  3. Find its own worksation name in the file
  4. Read the corresponding SSID and PSK specific to the workstation
  5. Create a wlan profile using the SSID and PSK

This sounded simple enough to me, but I had two issues with this method which I did't like:

  1. This method would require all of the PSKs and their associated information to be available to anyone who stumbled across the hidden Samba fileshare (not very likely, considering the client is an elementary school) but it still bothered me
  2. I would have to learn Microsoft Windows Powershell. Working on a Windows command line, or using Wordpad, or notepad are bad enough for me, nevermind learning an entire new scripting language just so that I could do a few tasks which I knew would be much easier and quicker to implement in a Bash shell script

And there it was! The answer was right there staring me in the face.

Use what you know and are already good at.

So I decided that I would need to write a Bash shell script and run it from on the server to do the parsing of the CSV file.



Samba to the Rescue
Samba is an amazing piece of open-source software. Not only because the developers had to reverse-engineer the Microsoft Windows networking protocols by capturing packets on the wire, and not only because it can make a Linux or Unix server look and function like a Microsoft Windows server, but especially because of the additional features and functionality it provides.

Two such additional features are the preexec and root preexec scripts options to a fileshare definition.

In Samba, when you define a fileshare, you may use these preexec script options to run a command or a script when a workstation attempts to connect to that fileshare. A preexec script can be used to do any number of things. A simple example is to log the date, time, machine name, user, and user's groups to a log file whenever they access a share. Adding a preexec script like this to the "netlogon" share makes sense so that all that information is automatically logged when a user logs into the Windows domain.

A slightly more complex example is using a preexec script that generates a custom logon script "on-the-fly" when a workstation connects to the netlogon share. When the workstation attempts to read the users' logon script (e.g.: \\fileserver\netlogon\janedoe.ps1), the preexec script is called, and access to the file is not handed over to the workstation until the preexec script is finished generating the janedoe.ps1 custom logon script. Typically this takes less than a few milliseconds, so it is unnoticable to the end users and it makes Samba very flexible.

Here is an example of a hidden, guest-accessible, Samba fileshare with a root preexec script defined:

[RuckusPSKs]
        comment = Ruckus PSKs Share
        path = /path/to/ruckuspsks
        guest ok = yes
        read only = yes
        browseable = no
        write list = @Administrators
        root preexec = /path/to/samba/scripts/createWlanProfile.sh %m

This short stanza in the /etc/samba/smb.conf file on our Samba server defines a Windows fileshare called "RuckusPSKs".

The share is hidden (browseable = no), allows guests to access it (guest ok = yes), and is read only (read only = yes), but users in the Administrators group may write to it (write list = @Administrators).

And finally, a root preexec script is defined (root preexec = /path/to/samba/scripts/createWlanProfile.sh) with one parameter (%m) before access to the share is handed off to the workstation.

In Samba, the %m parameter is the name of the workstation that is connecting to the share. A root prexex script was used because the script will need to read from and write to files on the server where users do not have access to. Also, because the FOG snapin on the workstations will be connecting to the RuckusPSKs fileshare as a service on the workstation, there will be no user involved, only a "machine account", so a root preexec script is the right choice here.

This is how the workstation-specific wlan Profile XML files will be generated!



Generating the Workstation-Specific XML File
OK, so now, we just need a short bash shell script to generate the workstation-specific XML file which the Windows netsh command will use.

The bash shell script, called createWlanProfile.sh will read the CVS file, find the workstation's name in the file and parse the line for the SSID and PSK. It will then write the XML information to a file using the workstation's name (e.g.: stulaptop1-wlanProfile.xml), and put it into the RuckusPSKs share for the workstation to use.



The CSV File
The CSV file has been mentioned several times, and before we can write a bash shell script to parse it, we are going to need to define what the file's format looks like. The information we need to have in this file minimal. We need the workstation's name, the SSID it is supposed to connect to, and we need the unique PSK for the workstation.

Here is the format of the CSV file that our Bash shell script will expect to see:

"Workstation Name","SSID","PSK"
"StuLaptop1","Student_Wifi","Y65$i(*)_"
"StuLaptop2","Student_Wifi","*uyTbs#2x"
"StuLaptop3","Student_Wifi","hGt43&!0V"
"StuLaptop4","Student_Wifi","lo*r%@-Lz"
"AdultLaptop1","Adults_Wifi","^8yTf#0(p"
"AdultLaptop2","Adults_Wifi","p(8Y6%f2D"
"AdultLaptop3","Adults_Wifi","&6G4L;op0"

You would probably not name your laptops, nor your wireless networks like that. Those names were chosen for display purposes.



Here is the Samba fileshare's root preexec script that will parse the CSV and generate the XML file:

createWlanProfile.sh

#!/bin/bash
#
# ---------------------------------------------------
# /path/to/samba/scripts/createWlanProfile.sh
#
# Generate custom Windows netsh wlan profile XML file
# via a Samba preexec option on a hidden share. This
# script will read data from a CSV file, find the
# machine's name and get the corresponding PSK.
#
#
# 20140626 - waa   William Arlofski
#                  waa@revpol.com
#                  http://www.revpol.com/
#
# ---------------------------------------------------
#
# This script is called by the preexec command in
# the [RuckusPSKs] share. It is called as:
#
# /path/to/samba/scripts/createWlanProfile.sh %m
#
# $1 in script = %m from Samba = machineName
#
# ---------------------------------------------------


# Make sure there was at least one parameter given
# ------------------------------------------------
if [ ${#} -eq 0 ]; then
        echo "Usage: $0 "
        exit
fi

# Lowercase the machine name
# --------------------------
machinename=$(echo $1 | tr [A-Z] [a-z])

profiledir="/path/to/ruckuspsks"
profile="$profiledir/${machinename}-wlanProfile.xml"
csv="/path/to/PSK-CSV-for-FOG/PSKS.csv"
data=$(grep -i \"${machinename}\" ${csv})
ssid=$(echo ${data} | cut -d, -f2 | tr -d \")
psk=$(echo ${data} | cut -d, -f3 | tr -d \")

profiletemplate="<?xml version="1.0" encoding="US-ASCII"?>
<WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1">
  <name>SchoolName ${ssid}</name>
  <SSIDConfig>
    <SSID>
      <name>${ssid}</name>
    </SSID>
  </SSIDConfig>
  <connectionType>ESS</connectionType>
  <connectionMode>auto</connectionMode>
  <autoSwitch>false</autoSwitch>
  <MSM>
    <security>
      <authEncryption>
        <authentication>WPA2PSK</authentication>
        <encryption>AES</encryption>
        <useOneX>false</useOneX>
      </authEncryption>
      <sharedKey>
        <keyType>passPhrase</keyType>
        <protected>false</protected>
        <keyMaterial>${psk}</keyMaterial>
      </sharedKey>
    </security>
  </MSM>
</WLANProfile>
"

# For testing, log each run
# -------------------------
# echo `date`" - ${machinename}" >> /tmp/wlantests.log

# Write wlan profile to \\fileserver\RuckusPSKs share
# ---------------------------------------------------
echo "${profiletemplate}" > ${profile}
unix2dos -q ${profile}
chmod 755 ${profile}



Testing the root preexex Script:
Testing the script is very simple. From our test Windows 7 laptop called "StuLaptop1", we open Windows Explorer and in the location field, type in the UNC name of the RuckusPSKs fileshare on the server:

\\fileserver\RuckusPSKs



If our root prexec script is working, Windows Explorer will show one file in this share: stulaptop1-wlanProfile.xml



And when we open the file in WordPad, the correct Wireless Profile Name, SSID and PSK for StuLaptop1 should be in the XML file:



The Windows Powershell Script
The final piece of this FOG snapin is the Windows Powershell script that will actually create the wlan wireless profile on our Windows 7 laptops.

Because our root preexec Bash script is doing all the hard work, the Powershell script will now only have to do the following two things:

  1. Determine the name of the workstation that the script is running on
  2. Create a wlan profile with the netsh.exe command using a workstation name-specific XML file on the server

Here is the wlanProfileFOGsnapin.ps1 Windows Powershell script that become our FOG snapin file:

#
# wlanProfileFOGsnapin.ps1
#
# waa - 20130630
# --------------
#
# Get the workstation's name, then use netsh to add
# a wlan profile from:
#
# \\fileserver\RuckusPSKs\$workstation-wlanProfile.xml
#
# The wlanProfile xml file is created on-the-fly by
# a root preexec option on the RuckusPSKs samba share
# ----------------------------------------------------
$Computer = Get-WmiObject -Class Win32_ComputerSystem
$Name = $Computer.Name
$lowerName = $Name.ToLower()
$wlanProfile = "\\fileserver\RuckusPSKs\$lowerName-wlanProfile.xml"
netsh wlan add Profile filename="$wlanProfile"

To test the Powershell script, open a Windows Command Prompt (CMD.exe) on our test Windows 7 laptop, and run the script manually:

C:\ > powershell -ExecutionPolicy Bypass ./wlanProfileFOGsnapin.ps1

In Microsoft's infinite wisdom, they created a new "powerful" scripting language and by default made it unable to run scripts! <facepalm>

To overcome this problem, the -ExecutionPolicy Bypass command line parameter is needed.

The last parameter of the Powershell command is the script file to run.

there should now be a new Wireless Network in the "Manage Wireless Networks" window. To get there do:

Control Panel --> Network and Internet --> Manage Wireless Networks


Putting the Pieces Together

Now we have all the pieces of our snapin, each one tested and working by itself. The final step is to make it into a FOG snapin.

*NOTE* This is currently a workaround I had to come up with becasue I was unable to get the FOG service on the Windows 7 laptops to run a Windows Powershell script directly. I have spoken with one of the FOG developers about this (Thanks Tom!), but since this small workaround works flawlessly, I had them hold off on any additional troubleshooting for now. If I can get FOG to run a Windows Powershell script directly, I will modify this howto to reflect the changes.

I can get the FOG service on the laptops to run DOS batch files, so I created a short DOS batch file that would be the actual FOG snapin file. This DOS batch file, called wlanProfile.bat will just call the Windows Powershell executable and run the wlanProfileFOGsnapin.ps1 Powershell script:

@ECHO OFF
REM
REM --------------------------------------------------
REM waa - 20140702
REM
REM - FOG Service does not seem to be able to run a
REM   powershell script directly. This DOS batch file
REM   calls powershell with the appropriate parameters
REM   and script name to run
REM --------------------------------------------------

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass \\fileserver\RuckusPSKs\wlanProfileFOGsnapin.ps1

The last parameter of the powershell.exe command is the UNC location of our Windows Powershell script. Because we had to make this batch file workaround, and could not use the Powershell script as our FOG snapin file directly, we needed to put the script somewhere all machines could access it. What better place than our hidden RuckusPSKs fileshare?



Creating the wlan Profile FOG Snapin
Log into administrative web interface of the FOG server:



Next, go to the snapins page by clicking the "package" icon labeled "Snap-in Management":



Select the "New Snapin" link on the left page and fill in the appropriate information. For the "Snapin File" field, just browse to the location of the wlanProfile.bat DOS batch file and upload it to the FOG server.



Click the "add" button to save your new FOG Snapin


Finished!

Your new wlan Profile FOG snapin is ready to be associated with, and deployed to Windows 7 laptops!




Post new comment

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <b> <i> <u> <strong> <cite> <code> <pre> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
L
r
y
u
P
#
Enter the code without spaces and pay attention to upper/lower case.