-
-
Save pudquick/fcbdd3924ee230592ab4 to your computer and use it in GitHub Desktop.
#!/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 = 'This SSID Should Be First' | |
next_to_last_SSID = 'This SSID Should Be Next To Last' | |
last_SSID = 'This SSID Should be Last' | |
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) |
jleomcdo
commented
Apr 8, 2022
via email
@jleomcdo you can absolutely do that, it's exactly what we're doing at my org.
Is there any script for monterey?
Is there any script for monterey?
The script @skoobasteeve posted a couple comments above works in Monterey, but requires the MacAdmins python3 package to be installed as Monterey doesn't have a default python installed. It works fine on all of our machines in our environment, including the ones running Monterey.
Is there any script for monterey?
The script @skoobasteeve posted a couple comments above works in Monterey, but requires the MacAdmins python3 package to be installed as Monterey doesn't have a default python installed. It works fine on all of our machines in our environment, including the ones running Monterey.
So how do we install all this with JAMF? Without installing xcode?
Is there any script for monterey?
The script @skoobasteeve posted a couple comments above works in Monterey, but requires the MacAdmins python3 package to be installed as Monterey doesn't have a default python installed. It works fine on all of our machines in our environment, including the ones running Monterey.
So how do we install all this with JAMF? Without installing xcode?
Push the macadmins/python package to your machines. Once it's installed, you can run any Python3 script from JAMF by using a shebang at the top of the script that points to the new instance of Python:
#!/Library/ManagedFrameworks/Python/Python3.framework/Versions/Current/bin/python3
Oh now i get it - thanks so much for your help 👍
Im trying this on Ventura. Installed python3 "python_recommended_signed-3.10.2.80694.pkg" when i run it im getting
Traceback (most recent call last):
File "/tmp/test2.py", line 18, in
CoreWLAN = load_objc_framework('CoreWLAN')
File "/tmp/test2.py", line 15, in load_objc_framework
return collections.namedtuple('AttributedFramework', loaded_classes.keys())(**loaded_classes)
File "/Library/ManagedFrameworks/Python/Python3.framework/Versions/3.10/lib/python3.10/collections/init.py", line 373, in namedtuple
raise ValueError('Type names and field names must be valid '
ValueError: Type names and field names must be valid identifiers: 'Foundation.__JSONDecoder'
@Tkimpton I think I got it working by adding a line to the load_objc_framework() function:
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)
# ADDED FOR PYTHON 3 CHANGE
# Avoids loading libraries that start with an underscore or reference a class e.g. Foundation.xx.
loaded_classes = dict(x for x in loaded_classes.items() if (not x[0].startswith('_')))
loaded_classes = dict(x for x in loaded_classes.items() if ('.' not in x[0]))
# print(loaded_classes_clean.keys())
return collections.namedtuple('AttributedFramework', loaded_classes.keys())(**loaded_classes)
The error occurs because namedtuple
doesn't like class references in a list of strings, and there were keys in the loaded_classes
dict with names like Foundation.xx
and Swift.xx
. I just removed all the items with keys that had a .
character in them.
Can you try it and see if it resolves the issue?
@skoobasteeve Works fine on Ventura here! Thank you
See further down
@jstaubr (CC: @skoobasteeve )
How did you manage to make it work on Ventura?
It works great on Monterey but on Ventura there were no change in the Preferred Network's list as I tested.
(It seems they deprecated the function. There's no UI either at System Preferences/Wi-Fi's to change the order of the networks. Only "Known Networks" window. )
Has anybody found a workaround for that?
@daydreamheart I'm sorry to inform you that Apple has "automated" the preferred wifi SSID choice in Ventura. It's not likely this will work going forward.https://support.apple.com/en-us/HT202831
@daydreamheart
Unfortunately @franton is right. I thought it was working because I was on the same network that I tried to make preferred and my CLI check returned the correct SSID.
I found out later last week that Ventura always returns the currently connected SSID as top of the preferred list via
networksetup -listpreferredwirelessnetworks en0
Well it was good while it lasted! At least in their new system they indicate that EAP networks get priority over WPA. Just wish we could retain some control.
Maybe some feedback to Apple is in order?