Posted on 12-16-2015 07:12 AM
Hello All.
I've been installing a configuration profile that sets up our wifi SSID and associates it with a machine certificate via the AD Certificate section. It associates the cert with the wifi config (so the user doesn’t get prompted) and overall has been working fine for quite some time.
I have one issue though. New networks place the network at the end of the preferred network list. Simple fix: should be to re-install right? Nope. The configuration profile places the network at the end of the Wifi Preferred Networks. I want it to be first.
There doesn’t seem to be a way to re-order it unless I remove the wifi network, then re-add it. That's fairly trivial to script, but that breaks the association with the computer’s certificate and the user now suffers.
Does anyone know how to programmatically re-order SSID preference without breaking the cert association OR how to add wifi and associate it with an existing machine cert so that the user isn't prompted?
Thanks!
Aaron
Posted on 12-16-2015 08:01 AM
I opened a case with Apple on this a couple months back. Unfortunately we ran into the same issue as what you are seeing. Below is a suggestion they came back with that we have not tried or tested as of yet.
You’re right that the -addpreferredwirelessnetworkatindex flag only works when adding new SSIDs, not rearranging existing ones. As for suggestions, I have a few. The SSID name format you’re seeing in the PreferredOrder array is actually the name of the Wi-Fi network converted from ASCII to hexadecimal. There’s two quick ways to turn that into something human readable. First way, find the “KnowNetworks” key, which contains a dictionary of the hex SSIDs and their individual settings. Within the individual settings, there’s a key called ‘SSIDString', which contains the plaintext version of the name. Example: <key>wifi.ssid.<464f4f></key> <dict> <key>AutoLogin</key> <false/> <key>Captive</key> <false/> <key>ChannelHistory</key> <array/> <key>Closed</key> <false/> <key>CollocatedGroup</key> <array/> <key>Disabled</key> <false/> <key>Passpoint</key> <false/> <key>PossiblyHiddenNetwork</key> <false/> <key>RoamingProfileType</key> <string>None</string> <key>SPRoaming</key> <false/> <key>SSID</key> <data> Rk9P </data> <key>SSIDString</key> <string>FOO</string> <key>SecurityType</key> <string>WPA Personal</string> <key>SystemMode</key> <false/> <key>TemporarilyDisabled</key> <false/> </dict> As you’ll see, this is for my example FOO network from earlier. Everywhere I see wifi.ssid.<464f4f>, it’s referring to this network. The other and in my opinion much easier way to do this is to find a nice online hex to ascii converter, and paste in the hex version of the name. Here’s one such site: http://www.unit-conversion.info/texttools/hexadecimal/. You can also work it in the other direction, entering the ASCII name that you know and converting it to the hex name you should be hunting for. In theory, you could also do the conversion with the command line tool ‘iconv’, but it requires creating input files containing the data to be converted and was a bit convoluted for my taste. ‘man iconv’ if you want to try it out. Now, after you know the hex name of the network, you should be able to modify the .plist by hand. Just make sure your network is the first item listed in the PreferredOrder array, and that it only appears once in the list. In my testing, I found that this method worked to reorder the preferred network list, and that my change survived reboots, toggling of the Wi-Fi interface, and many other attempts to test if the system would revert to the original order. The official answer is still “make the change in the GUI”, so there’s no guarantee this hand editing method will continue to work with later OS releases. As such, if you plan on using this workaround for now, I highly suggest testing that it continues to work when new beta builds of the OS are made available.
Posted on 12-16-2015 06:08 PM
OK. I found the plist and the array PreferredOrder where i see the hex form of my network name. I'll be using plistbuddy to see if I can re-order the items in PreferredOrder by deleting the existing item I want to move, then inserting it at location 0. I just need to verify that it works.
When modifying plists with plistbuddy, is there a preferred method for making sure changes are properly applied and stick? Any cfprefsd killing, or other things to do before, during, after? Target OS's are 10.9 and 10.10.
Thanks!
Aaron
Posted on 12-22-2015 10:02 AM
This is a script I made a few weeks ago, It does remove and re-add the SSID from the preferred network list but I've noticed as long as they are currently connected to that SSID they were never kicked off. However we use Configurations to push our networks out. Is it possible for you to add that certificate to be system-based rather than SSID specific?
If so then this script I made could be used:
https://jamfnation.jamfsoftware.com/discussion.html?id=17858
Posted on 12-22-2015 10:09 AM
Heh..delete this one. Double posted....
Posted on 12-22-2015 10:54 AM
I've been using a python script made by pudquick on GitHub, linked here
I run this to change the order of preferred networks:
#!/usr/bin/python
# As written, this requires the following:
# - OS X 10.6+ (may not work in 10.10, haven't tested)
# - python 2.6 or 2.7 (for collections.namedtuple usage, should be fine as default python in 10.6 is 2.6)
# - pyObjC (as such, recommended to be used with native OS X python install)
# Only tested and confirmed to work against 10.9.5
# Run with root
import objc, ctypes.util, os.path, collections
from Foundation import NSOrderedSet
preferred_SSID = 'yourSSIDhere'
next_to_last_SSID = 'yourSSIDhere'
last_SSID = 'yourSSIDhere'
def load_objc_framework(framework_name):
# Utility function that loads a Framework bundle and creates a namedtuple where the attributes are the loaded classes from the Framework bundle
loaded_classes = dict()
framework_bundle = objc.loadBundle(framework_name, bundle_path=os.path.dirname(ctypes.util.find_library(framework_name)), module_globals=loaded_classes)
return collections.namedtuple('AttributedFramework', loaded_classes.keys())(**loaded_classes)
# Load the CoreWLAN.framework (10.6+)
CoreWLAN = load_objc_framework('CoreWLAN')
# Load all available wifi interfaces
interfaces = dict()
for i in CoreWLAN.CWInterface.interfaceNames():
interfaces[i] = CoreWLAN.CWInterface.interfaceWithName_(i)
# Repeat the configuration with every wifi interface
for i in interfaces.keys():
# Grab a mutable copy of this interface's configuration
configuration_copy = CoreWLAN.CWMutableConfiguration.alloc().initWithConfiguration_(interfaces[i].configuration())
# Find all the preferred/remembered network profiles
profiles = list(configuration_copy.networkProfiles())
# Grab all the SSIDs, in order
SSIDs = [x.ssid() for x in profiles]
# Check to see if our preferred SSID is in the list
if (preferred_SSID in SSIDs):
# Apparently it is, so let's adjust the order
# Profiles with matching SSIDs will move to the front, the rest will remain at the end
# Order is preserved, example where 'ssid3' is preferred:
# Original: [ssid1, ssid2, ssid3, ssid4]
# New order: [ssid3, ssid1, ssid2, ssid4]
profiles.sort(key=lambda x: x.ssid() == preferred_SSID, reverse=True)
# Now we move next_to_last_SSID to the end
profiles.sort(key=lambda x: x.ssid() == next_to_last_SSID, reverse=False)
# Now we move last_SSID to the end (bumping next_to_last_SSID)
profiles.sort(key=lambda x: x.ssid() == last_SSID, reverse=False)
# Now we have to update the mutable configuration
# First convert it back to a NSOrderedSet
profile_set = NSOrderedSet.orderedSetWithArray_(profiles)
# Then set/overwrite the configuration copy's networkProfiles
configuration_copy.setNetworkProfiles_(profile_set)
# Then update the network interface configuration
result = interfaces[i].commitConfiguration_authorization_error_(configuration_copy, None, None)
The problem I was having was with our users computers connecting to our guest wifi over our employee wifi, so I also run a script to disable/re-enable wifi so they actually connect to the preferred network right away:
networksetup -setairportpower en0 off
networksetup -setairportpower en0 on
I just add both of these scripts to a single policy and place it in Self Service.
Edit: Added link to source for pudquick's GitHub
Posted on 02-25-2022 05:01 AM
Has anyone tried porting this python script to v3 yet with python being removed in macOS 12.3? I'm not a Python programmer but I tried my basic knowledge at porting the script from python2 to python 3 and manually installing python3 on macOS 12.3 beta. It appears python3 did not have objc so I installed objc with pip3. It appears "maybe?" the Python3 objc does not contain what is needed to access natively like the macOS provided python v2.x did as it appears broken. I also run the script through the python v2 to v3 converter 2to3 beforehand. Just curious if there is a new method scripted that someone has developed, swift/bash/other that is seamless. I noticed the dockutil dev rewrote dockutil in swift in the past few weeks and it's in beta. (We swapped to it and the beta seems to be fine on macOS 12.3) Several methods I have found could "disconnect" WiFi while changing the preferred order.
Posted on 04-01-2022 11:53 AM
I just started looking into this today. I also use this python script to set wireless network order. If anyone finds a different solution, please post.
Posted on 04-01-2022 12:40 PM
Please refer to post https://community.jamf.com/t5/jamf-pro/re-order-wifi-preferred-networks/m-p/262367/highlight/true#M2... in this thread for a Python 3 compatible script to re-order SSIDs while preserving 802.1x configuration, and the link to the necessary Python 3 install.
Posted on 04-04-2022 05:04 AM
Thank you for sharing this. One other silly question.... I have downloaded the python3 files from https://github.com/macadmins/python.
Now how do I "install" it? It's just the source files.
Posted on 01-10-2024 07:35 PM
I just noticed I'd never responded to @jleomcdo 's question on installing the MacAdmins Python 3 framework, so to close the loop on that for anyone else wondering you need to download the installer package from the Releases link: https://github.com/macadmins/python/releases
Posted on 12-22-2015 11:06 AM
That script worked perfectly on my 10.10 system. Thanks for sharing!
Edit For those interested I found the link to the script on pudquick's github.
https://gist.github.com/pudquick/fcbdd3924ee230592ab4
Aaron
Posted on 12-22-2015 11:35 AM
@FritzsCorner
No problem! It completely solved our issue as well.
Let Forgor(/pudquick) know you owe him a beer in the MacAdmins Slack channel if you're a member.
Posted on 01-28-2016 01:31 AM
This is a lifesaver!!!
Thank you - been looking for a way to do this for some time. I added a sleep 5 to the wireless enable/disable as was not always seeing the power get switched back on to the wireless card after it was switched off. Now it works on all our hardware. Many thanks.
Posted on 05-18-2016 01:52 PM
THANK YOU!!!!!!!!!!!!!!!!! I WILL BE IMPLEMENTING TOMORROW!!! I have users that have been driving me mad with OS X's Wifi lists. I can't wait to run it.
Posted on 01-04-2017 02:19 PM
12/22/15 at 12:54 PM by Anthonyr
Your solution also worked for me!
Thanks!
Posted on 02-16-2017 04:42 PM
This thing worked like a champ on 10.12. Thank you so much!!
Posted on 02-28-2017 06:05 AM
Legend! Script works perfectly! Thank you!
Posted on 03-16-2017 05:29 AM
This is great! Exactly what I needed! Thanks!
Posted on 03-17-2017 07:26 AM
I tweaked pudquick's script so that you just have to edit the array of preferred SSIDs rather than modify the code if you have more than 3 in your environment:
#!/usr/bin/python
# As written, this requires the following:
# - OS X 10.6+ (has been reported working through 10.12.3)
# - python 2.6 or 2.7 (for collections.namedtuple usage, should be fine as default python in 10.6 is 2.6)
# - pyObjC (as such, recommended to be used with native OS X python install)
# Run with root
import objc, ctypes.util, os.path, collections
from Foundation import NSOrderedSet
# List of preferred SSIDs in priority order - edit/add/delete as needed
PreferredSSIDs = ["SSID_1", "SSID_2", "SSID_3"]
def load_objc_framework(framework_name):
# Utility function that loads a Framework bundle and creates a namedtuple where the attributes are the loaded classes from the Framework bundle
loaded_classes = dict()
framework_bundle = objc.loadBundle(framework_name, bundle_path=os.path.dirname(ctypes.util.find_library(framework_name)), module_globals=loaded_classes)
return collections.namedtuple('AttributedFramework', loaded_classes.keys())(**loaded_classes)
# Load the CoreWLAN.framework (10.6+)
CoreWLAN = load_objc_framework('CoreWLAN')
# Load all available wifi interfaces
interfaces = dict()
for i in CoreWLAN.CWInterface.interfaceNames():
interfaces[i] = CoreWLAN.CWInterface.interfaceWithName_(i)
# Repeat the configuration with every wifi interface
for i in interfaces.keys():
# Grab a mutable copy of this interface's configuration
configuration_copy = CoreWLAN.CWMutableConfiguration.alloc().initWithConfiguration_(interfaces[i].configuration())
# Find all the preferred/remembered network profiles
profiles = list(configuration_copy.networkProfiles())
# Grab all the SSIDs, in order
SSIDs = [x.ssid() for x in profiles]
# Loop through PreferredSSIDs list in reverse order sorting each entry to the front of profiles array so it
# ends up sorted with PreferredSSIDs as the first items.
# Order is preserved for other SSIDs, example where PreferredSSIDs is [ssid3, ssid4]:
# Original: [ssid1, ssid2, ssid3, ssid4]
# New order: [ssid3, ssid4, ssid1, ssid2]
for aSSID in reversed(PreferredSSIDs):
profiles.sort(key=lambda x: x.ssid() == aSSID, reverse=True)
# Now we have to update the mutable configuration
# First convert it back to a NSOrderedSet
profile_set = NSOrderedSet.orderedSetWithArray_(profiles)
# Then set/overwrite the configuration copy's networkProfiles
configuration_copy.setNetworkProfiles_(profile_set)
# Then update the network interface configuration
result = interfaces[i].commitConfiguration_authorization_error_(configuration_copy, None, None)
Posted on 05-19-2017 11:36 AM
This script it great :)
I'd like to be able to set the SSID in JSS. I know that this can be done by using $4, $5, $6 etc but what else is required in the script to get this to work?
# List of preferred SSIDs in priority order - edit/add/delete as needed
PreferredSSIDs = ["$4", "$5", "$6"]
Posted on 06-07-2017 10:48 AM
@MatG Sorry, didn't notice your question earlier. The $4, $5, and $6 parameters are directly accessible as variables in a shell bash script, but in Python you access them by using sys.argv[parameter#]
. See this post for more info: Adding a Python Script to JSS
Posted on 06-08-2017 06:24 AM
This is great! Just tested this and confirmed on a 10.12.5 machine that our school's Wi-Fi network successfully is moved to the top while the rest of the Wi-Fi network order remains the same.
Posted on 09-12-2017 02:47 PM
Hi all,
I'm getting this error when its ran on some machines, but not all:
[STEP 1 of 4]
Executing Policy Fix Wi-Fi network order
[STEP 2 of 4]
Running script Set_Wifi_Order.sh...
Script exit code: 1
Script result: Traceback (most recent call last):
File "/Library/Application Support/JAMF/tmp/Set_Wifi_Order.sh", line 35, in
profiles = list(configuration_copy.networkProfiles())
TypeError: 'NoneType' object is not iterable
Error running script: return code was 1.
[STEP 3 of 4]
[STEP 4 of 4]
Any ideas? I only have one SSID listed:
#!/usr/bin/python
# As written, this requires the following:
# - OS X 10.6+ (has been reported working through 10.12.3)
# - python 2.6 or 2.7 (for collections.namedtuple usage, should be fine as default python in 10.6 is 2.6)
# - pyObjC (as such, recommended to be used with native OS X python install)
# Run with root
import objc, ctypes.util, os.path, collections
from Foundation import NSOrderedSet
# List of preferred SSIDs in priority order - edit/add/delete as needed
PreferredSSIDs = ["ILAnetwork"]
def load_objc_framework(framework_name):
# Utility function that loads a Framework bundle and creates a namedtuple where the attributes are the loaded classes from the Framework bundle
loaded_classes = dict()
framework_bundle = objc.loadBundle(framework_name, bundle_path=os.path.dirname(ctypes.util.find_library(framework_name)), module_globals=loaded_classes)
return collections.namedtuple('AttributedFramework', loaded_classes.keys())(**loaded_classes)
# Load the CoreWLAN.framework (10.6+)
CoreWLAN = load_objc_framework('CoreWLAN')
# Load all available wifi interfaces
interfaces = dict()
for i in CoreWLAN.CWInterface.interfaceNames():
interfaces[i] = CoreWLAN.CWInterface.interfaceWithName_(i)
# Repeat the configuration with every wifi interface
for i in interfaces.keys():
# Grab a mutable copy of this interface's configuration
configuration_copy = CoreWLAN.CWMutableConfiguration.alloc().initWithConfiguration_(interfaces[i].configuration())
# Find all the preferred/remembered network profiles
profiles = list(configuration_copy.networkProfiles())
# Grab all the SSIDs, in order
SSIDs = [x.ssid() for x in profiles]
# Loop through PreferredSSIDs list in reverse order sorting each entry to the front of profiles array so it
# ends up sorted with PreferredSSIDs as the first items.
# Order is preserved for other SSIDs, example where PreferredSSIDs is [ssid3, ssid4]:
# Original: [ssid1, ssid2, ssid3, ssid4]
# New order: [ssid3, ssid4, ssid1, ssid2]
for aSSID in reversed(PreferredSSIDs):
profiles.sort(key=lambda x: x.ssid() == aSSID, reverse=True)
# Now we have to update the mutable configuration
# First convert it back to a NSOrderedSet
profile_set = NSOrderedSet.orderedSetWithArray_(profiles)
# Then set/overwrite the configuration copy's networkProfiles
configuration_copy.setNetworkProfiles_(profile_set)
# Then update the network interface configuration
result = interfaces[i].commitConfiguration_authorization_error_(configuration_copy, None, None)
Posted on 10-27-2017 07:45 AM
@rqomsiya Sounds to me like those computers either don't have the SSID (profile) configured or don't have a WIFI interface.
Posted on 03-19-2021 09:24 AM
sdagley's version of the script works perfectly on OSX 11.x
Posted on 04-30-2021 11:27 AM
FYI: In Big Sur, Apple moved the known networks from /Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist to /Library/Preferences/com.apple.wifi.known-networks.plist file.
Posted on 01-13-2022 08:26 AM
Anyone still using a script to manage order of preferred wireless networks in MacOS 11 or above? If so is it working?
Posted on 01-21-2022 06:17 AM
#!/bin/bash
##############################################################
# Script : Orders Mac Wi-Fi
##############################################################
# To order we remove and replace. Replace is always top of the list
SSID=$4
Security_Type=$5
networksetup -removepreferredwirelessnetwork en0 "$SSID"
networksetup -addpreferredwirelessnetworkatindex en0 "$SSID" 0 $Security_Type
We use this script and set the S4 & S5 values in the Script Parameters in the Script Payload of the policy.
The script removes and adds and when adding the network goes to the top of the list, even if you are connected to the Wi-Fi network being removed and added we see no loss of connection.
Posted on 01-21-2022 08:04 AM
@MatG That will break any 802.1x configuration that was in effect for the SSID.
01-21-2022 08:34 AM - edited 01-21-2022 08:37 AM
@sdagley We have it in place on our Monterey devices and its not causing any issues and was thoroughly tested with numerous devices. We are not getting any reports of loss of connection.
Posted on 01-21-2022 10:44 AM
@MatG And you're using 802.1x auth? That's good to hear as it definitely didn't work through macOS Sierra, and that's what created the need for the Python scripts posted below.
01-21-2022 01:37 PM - edited 01-21-2022 01:48 PM
My policy/script works on Monterey, Big Sur and Catalina laptops.
Our policy runs once a day as needed (at check-in) and is scoped to only Macs laptops that do not already have our WLAN SSID in the top "favorites" location (index 0 of the array). This verification is performed via an EA.
The policy payload is a Z Shell script. It runs well on both Intel and Apple ARM CPUs and all our supported OSs.
We leverage 802.1x (EAP-TLS) and we use our JSS as a SCEP proxy server for machine certs issued by our interal ADCS.
Our RADIUS WALN security is a Cisco ISE appliance running 3.0.0.458
Posted on 02-02-2022 03:19 PM
@dstranathan have you already shared the script somewhere, this sounds good, with python going away in 12.3 we're looking at alternatives
02-25-2022 09:28 AM - edited 02-25-2022 09:30 AM
I have been working on updating mine. Here is the latest Python3 version I am testing. This version requires MacAdmins Managed Python (it includes PyObjC).
#!/Library/ManagedFrameworks/Python/Python3.framework/Versions/Current/bin/python3
import objc, ctypes.util, os.path, collections
from Foundation import NSOrderedSet
preferred_SSID = 'MY_CORP_WLAN' ## Put your SSID here
def load_objc_framework(framework_name):
loaded_classes = dict()
framework_bundle = objc.loadBundle(framework_name, bundle_path=os.path.dirname(ctypes.util.find_library(framework_name)), module_globals=loaded_classes)
loaded_classes = dict(x for x in loaded_classes.items() if (not x[0].startswith('_')))
return collections.namedtuple('AttributedFramework', loaded_classes.keys())(**loaded_classes)
CoreWLAN = load_objc_framework('CoreWLAN')
interfaces = dict()
for i in CoreWLAN.CWInterface.interfaceNames():
interfaces[i] = CoreWLAN.CWInterface.interfaceWithName_(i)
for i in interfaces.keys():
configuration_copy = CoreWLAN.CWMutableConfiguration.alloc().initWithConfiguration_(interfaces[i].configuration())
profiles = list(configuration_copy.networkProfiles().array())
SSIDs = [x.ssid() for x in profiles]
if (preferred_SSID in SSIDs):
profiles.sort(key=lambda x: x.ssid() == preferred_SSID, reverse=True)
profile_set = NSOrderedSet.orderedSetWithArray_(profiles)
configuration_copy.setNetworkProfiles_(profile_set)
result = interfaces[i].commitConfiguration_authorization_error_(configuration_copy, None, None)
else:
print("\nThe SSID '{}' was not located in the macOS Preferred Networks list.".format(preferred_SSID))
Posted on 02-25-2022 01:17 PM
Thanks for the info and work on this, have a great weekend!
Posted on 03-05-2022 10:21 AM
Would you mind sharing your EA as well?
Posted on 03-08-2022 05:47 AM
Hi @dstranathan
I've installed python from https://github.com/macadmins/python, but then trying to run script still getting errors:
/wifi_prefered.sh: line 2: import: command not found
from: can't read /var/mail/Foundation
/wifi_prefered.sh: line 4: preferred_SSID: command not found
/wifi_prefered.sh: line 5: syntax error near unexpected token `('
/wifi_prefered.sh: line 5: `def load_objc_framework(framework_name):'
Posted on 03-24-2022 07:52 AM
Ill have to dig into this. I just tested it on Catalina, Big Sur and Monterey and it is working here. Im not a 'Python guy' at all (trying to get approval for more formal classes in 2022 actually!).
Some of this script is based on Michael Lynn's efforts: https://gist.github.com/pudquick/fcbdd3924ee230592ab4
Posted on 03-24-2022 06:29 AM
@dstranathan im getting the following error
File "/private/tmp/test4.sh", line 6
loaded_classes = dict()
^
IndentationError: expected an indented block