I was recently working with a customer when the subject of WiFi profiles came up. Now this was a curious situation because the machines in question needing a WiFi profile assigned to them were not domain joined and currently at least Intune wasn’t on the table for them. Of course it seemed like it might be a job for the WiFi profile feature in ConfigMgr however after asking around and some testing I found it was very buggy and the documentation from Microsoft seems somewhat misleading.
To deploy profiles to Android, iOS, Windows Phone, and enrolled Windows 8.1 or later devices, these devices must be enrolled in Microsoft Intune. For information about how to get your devices enrolled, see Enroll devices for management in Intune.
Now its hard to be 100% sure but between how difficult it was to get it to work in a lab environment it seems the intention is that really it’s not ‘made’ for that. Now several solutions were tossed around before finally deciding on scripts for various reasons.
- We wanted to programmatically generate the information around the XML to make it easier to generate new profiles
- We wanted the passwords to not be stored locally on the clients for any longer than absolutely necessary
- We wanted to ensure that we could deploy them quickly as the devices would only be on a wired connection for a short period of time
This lead us to a run scripts solutions. For anyone not yet familiar with run scripts its a new feature in 1806 and I may do some additional blog posts about it at a later date. While working with this in the run-scripts space I ran into a few challenges. The first challenge was that if you parameterized the SSID and the password, currently the Secure String parameter type is not supported by ConfigMgr run-scripts which means the parameter for the script is sent in plain text and when it reaches the client the action is called in plain text meaning the parameter used is visible in the scripts log file found in C:\windows\ccm\logs\scripts.log
In order to resolve this I tried to come up with several different methods and a ended up with a few different variations of roughly the same script. Please note this script is only workable with WPA2 personal (pre-shared keys). You could however easily adapt this idea to do any type of profile you would just need to update or modify the XML. You can get the XML for any wireless profile that is on a laptop by using the following command.
netsh wlan show profile
This will show you all of the user based profiles and all of the assigned group policy WiFi profiles as well for the machine. You can of course take this a step further and export the XML that is used to store these profiles in plain text using the following command, please note again that using this command WILL store the XML in plane text. One could in theory use this to write a script that would capture every wireless network a users laptop has ever logged into and its password and save all of that information to a network share in plain text.
It’s probably worth noting that showing something like this should serve as a reminder to double check all of the RBAC assignments in your environment. It’s also worth noting this method can delete unwanted/bloated/non-corporate profiles if you so desired, or all of them which would be bad, probably. I didn’t use it in the line above but as I mentioned you can command the password to be exported in plain text using ‘key=clear’ like so
netsh wlan export profile name=PROFILENAME folder=C:\FOLDERPATH key=clear
If you open the XML file this generates you’ll find a value in the XML called ‘keyMaterial’ and this will have the pre-shared key within it. You can read more about the XML properties here.
Now with all of this being said you might be thinking ‘man this is a really insecure and really impractical method to build wireless profiles why even bother’ The answer because it works and it works in a really specific use case very quickly and efficiently. The script is available for download from the SCConfigMgr GitHub repository. I’ll also add the disclaimer to use this script at your own risk and to make smart choices in its use because no it’s not the most ‘secure’ thing on the planet. However, I’ll also note that while your password MAY get logged in the scripts folder, or in the PowerShell ScriptStore regardless of your choice you can export the wlan profile running as a non elevated admin user and while I’ve not tested it but I would assume that means you don’t need to be an admin to export the info.
The lesson that we should take away from this is maybe it’s time to stop using pre-shared passwords for wireless for anything but a guest network and everything else should be certificate based. The script is below and can be copied into the run scripts section of ConfigMgr. If you haven’t used the Run Scripts feature before you can read another post I put together regarding getting the next maintenance window using PowerShell and see the second half of the post for information on how to create and run a script. If you do it successfully you should get an output that looks like this upon script completion.
<# .SYNOPSIS This Script is used to build a WiFi XML Template for WPAPSK .DESCRIPTION This is set up using basic connection rules for WPA-PSK and accepts parameters. The idea of this is to allow you to quickly deploy wireless profiles to machines using the run-scripts module in ConfigMgr. Please note that doing this WILL cause a network password to temporarily be stored in PLAIN TEXT. .PARAMETER ProfileName This parameter is used to submit the SSID of the network you will be connecting too. This parameter is CASE SENSITIVE. This is because the content is converted into a hex string value. Currently this is only for SSID's that ARE being broadcasted. .PARAMETER Key This paramter is used to submit the passsword for the SSID network. This password is stored temporarily in plain text on the machine when the VM is generated. The password is NOT stored in the script repository for the client. .NOTES FileName: Set-WiFiProfile.ps1 Author: Jordan Benzing Contact: @JordanTheItGuy Created: 2018-09-10 Updated: 2018-09-10 Version history: 1.0.8 - (2018-09-10) Script created This is NOT an extremely secure method for creating profiles however if you read the information in my post you'll notice that in reality wireless profiles aren't stored securely on computers anyways. #> #region ConfigurationBlock <# This section is currently commented out if you want to remove the parameterization out comment out the parameter region of the script and uncommnet the configuration region. Note if you elect to use this code block you will be storing information in plain text within the script and will also likely want to un-comment the scheduledtask region to clean the scripts store. $ProfileName = YourSSIDHere $Key= YourWirelessKeyHere #> #endregion ConfigurationBlock #region ParameterBlock #If you elect to hardcode the profiles comment this section of code out and use the configuration block region instead. [CmdletBinding(SupportsShouldProcess=$true)] param (
[parameter(Mandatory = $true)]
[string]$ProfileName,
[parameter(Mandatory = $true)]
[String]$Key ) #endregion ParameterBlock Begin {} Process { $conversionValue = $ProfileName.ToCharArray() #Converts the Provided profile to a char array that is then used to convert to Hex. foreach($letter in $conversionValue){$HexKey = $HexKey + ” ” + [System.String]::Format(“{0:X}”, [System.Convert]::ToUInt32($letter))} #Converts the string into a HEX value $HexKey = $HexKey.ToSTring() #Converts the bytecollection object to a String $HexKey = $HexKey.replace(‘ ‘,”) #removes spaces from the string. $data = @” <?xml version=”1.0″?> <WLANProfile xmlns=”https://www.microsoft.com/networking/WLAN/profile/v1″> <name>$ProfileName</name> <SSIDConfig> <SSID> <hex>$HexKey</hex> <name>$ProfileName</name> </SSID> </SSIDConfig> <connectionType>ESS</connectionType> <connectionMode>auto</connectionMode> <MSM> <security> <authEncryption> <authentication>WPA2PSK</authentication> <encryption>AES</encryption> <useOneX>false</useOneX> </authEncryption> <sharedKey> <keyType>passPhrase</keyType> <protected>false</protected> <keyMaterial>$Key</keyMaterial> </sharedKey> </security> </MSM> <MacRandomization xmlns=”https://www.microsoft.com/networking/WLAN/profile/v3″> <enableRandomization>false</enableRandomization> <randomizationSeed>3386856935</randomizationSeed> </MacRandomization> </WLANProfile> “@ #Generates the Here string for a common wifi profile type. New-Item C:\Windows\Temp\WLANProfile.xml -ItemType file -Force -Value $data | Out-Null #Creates the .XML file that will be used to import the profile temporarily. <# This block will create a scheduled task that will remove all scripts stored in the script store repostory This script section is useful if you decided to hardcode the SSID tag and the wifi password parameter **DEVNOTE** – I may later develop this to parse the ‘Scripts’ log to find the GUID of the script and then instead only remove the script that ran this if(Get-ScheduledTask -TaskName ‘Clean ScriptStore’){Unregister-ScheduledTask -TaskName ‘Clean ScriptStore’ -Confirm:$false} $Command = “if(test-path -path C:\Windows\ccm\scriptstore){Get-ChildItem -Path C:\Windows\CCM\ScriptStore\ -Filter *.ps1 | ForEach-Object {Remove-Item -Path `$_.FullName -Force}}” & schtasks /create /ru “System” /sc ONCE /ST (Get-Date).AddMinutes(2).ToString(‘HH:mm:ss’) /tn “Remove Scripts log” /TR “PowerShell.Exe -Command $Command” #> #Generates the XML file locally in the TEMP directory this can be changed to use a different location. netsh wlan add profile filename=”C:\Windows\Temp\WLANProfile.xml” #imports the .XML file from the temp directory to install the WLAN Profile Remove-Item C:\Windows\Temp\WLANProfile.xml #Removes the XML file that was created and used from the machine }
…its a new feature in 1806…
1706 pre-release, 1802 non pre-release.