v1.5.0 = Support for Macs with 10 character serial numbers

+ Updated to support 2021 Macs with 10 character serial numbers
This commit is contained in:
Zack T
2022-01-06 15:20:40 -07:00
parent 21511d0583
commit 2e5fda160c

View File

@@ -3,26 +3,27 @@
"""
Script Name: Install-BomgarJumpClient.py
By: Zack Thompson / Created: 3/2/2020
Version: 1.4.2 / Updated: 1/4/2022 / By: ZT
Version: 1.5.0 / Updated: 1/5/2022 / By: ZT
Description: Installs a Bomgar Jump Client with the passed parameters
"""
import argparse
import objc
import os
import plistlib
import re
import shlex
import subprocess
import sys
from Cocoa import NSBundle
from SystemConfiguration import SCDynamicStoreCopyConsoleUser
from xml.etree import ElementTree
from Cocoa import NSBundle, NSString
import objc
import requests
from SystemConfiguration import SCDynamicStoreCopyConsoleUser
def execute_process(command):
def execute_process(command, input=None):
"""
A helper function for subprocess.
@@ -36,15 +37,20 @@ def execute_process(command):
# Validate that command is not a string
if not isinstance(command, str):
raise TypeError('Command must be a str type')
raise TypeError("Command must be a str type")
# Format the command
command = shlex.split(command)
# Run the command
process = subprocess.Popen( command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
process = subprocess.Popen( command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
shell=False, universal_newlines=True )
(stdout, stderr) = process.communicate()
if input:
(stdout, stderr) = process.communicate(input=input)
else:
(stdout, stderr) = process.communicate()
return {
"stdout": (stdout).strip(),
@@ -54,11 +60,13 @@ def execute_process(command):
}
def get_system(attribute):
"""A helper function to get specific system attributes.
def get_system_info():
"""
A helper function to get specific system attributes.
Credit: Mikey Mike/Froger/Pudquick/etc
Source: https://gist.github.com/pudquick/c7dd1262bd81a32663f0
Notes: Modified from source
Args:
attribute: The system attribute desired.
@@ -67,68 +75,79 @@ def get_system(attribute):
stdout: The system attribute value.
"""
IOKit_bundle = NSBundle.bundleWithIdentifier_('com.apple.framework.IOKit')
IOKit_bundle = NSBundle.bundleWithIdentifier_("com.apple.framework.IOKit")
functions = [
("IORegistryEntryCreateCFProperty", b"@I@@I"),
("IOServiceGetMatchingService", b"II@"),
("IOServiceMatching", b"@*"),
("IORegistryEntryCreateCFProperty", b"@I@@I")
("IOServiceMatching", b"@*")
]
objc.loadBundleFunctions(IOKit_bundle, globals(), functions)
def io_key(keyname):
return IORegistryEntryCreateCFProperty(
IOServiceGetMatchingService(
0, IOServiceMatching(
"IOPlatformExpertDevice".encode("utf-8"))), keyname, None, 0)
def io_key(key_name, service_name="IOPlatformExpertDevice"):
service = IOServiceMatching(service_name.encode("utf-8"))
key = NSString.stringWithString_(key_name)
return IORegistryEntryCreateCFProperty(IOServiceGetMatchingService(0, service), key, None, 0)
# def get_hardware_uuid():
# return io_key("IOPlatformUUID".encode("utf-8"))
def get_hardware_uuid():
return io_key("IOPlatformUUID")
def get_hardware_serial():
return io_key("IOPlatformSerialNumber")
# def get_board_id():
# return str(io_key("board-id".encode("utf-8"))).rstrip('\x00')
def get_board_id():
try:
return bytes(io_key("board-id")).decode().rstrip("\x00")
except TypeError:
return ""
options = {'serial' : get_hardware_serial #,
# 'uuid' : get_hardware_uuid,
# 'boardID' : get_board_id
def get_model_id():
return bytes(io_key("model")).decode().rstrip("\x00")
def lookup_model(lookup_code):
xml = requests.get("https://support-sp.apple.com/sp/product?cc={}".format(lookup_code)).text
try:
tree = ElementTree.fromstringlist(xml)
return tree.find(".//configCode").text
except ElementTree.ParseError as err:
print("Failed to retrieve model name: {}".format(err.strerror))
return ""
serial_number = get_hardware_serial()
sn_length = len(serial_number)
model = ""
if sn_length == 10:
results = execute_process("/usr/sbin/ioreg -arc IOPlatformDevice -k product-name")
if results["success"]:
plist_contents = plistlib.loads(results["stdout"].encode())
model = plist_contents[0].get("product-name").decode().rstrip("\x00")
elif sn_length == 12:
model = lookup_model(serial_number[-4:])
elif sn_length == 11:
model = lookup_model(serial_number[-3:])
return {
"serial_number": serial_number,
"uuid": get_hardware_uuid(),
"board_id": get_board_id(),
"model_id": get_model_id(),
"model_friendly": model
}
return options[attribute]()
def get_model(serial_number):
"""A helper function to get the friendly model.
Args:
serial_number: Devices' Serial Number.
Returns:
stdout: friendly model name or "".
"""
if len(serial_number) == 12:
lookup_code = serial_number[-4:]
elif len(serial_number) == 11:
lookup_code = serial_number[-3:]
else:
print("Unexpected serial number length: {}".format(serial_number))
return ""
lookup_url = "https://support-sp.apple.com/sp/product?cc={}".format(lookup_code)
xml = requests.get(lookup_url).text
try:
tree = ElementTree.fromstringlist(xml)
return tree.find('.//configCode').text
except ElementTree.ParseError as err:
print("Failed to retrieve model name: {}".format(err.strerror))
return ""
def mount(pathname):
"""A helper function to mount a volume and return the mount path.
"""
A helper function to mount a volume and return the mount path.
Args:
pathname: Path to a dmg to mount.
Returns:
stdout: Returns the path to the mounted volume.
"""
@@ -138,14 +157,14 @@ def mount(pathname):
results = execute_process(mount_cmd)
if not results['success']:
if not results["success"]:
print("ERROR: failed to mount: {}".format(pathname))
print(results['stdout'])
print(results['stderr'])
print(results["stdout"])
print(results["stderr"])
sys.exit(2)
# Read output plist.
xml = plistlib.loads(results['stdout'].encode())
xml = plistlib.loads(results["stdout"].encode())
# Find mount point.
for part in xml.get("system-entities", []):
@@ -155,12 +174,13 @@ def mount(pathname):
print("mounting {} failed: unexpected output from hdiutil".format(pathname))
def main():
print('\n***** install_Bomgar process: START *****\n')
print("\n***** install_Bomgar process: START *****\n")
# Reset sys.argv to reformat parameters being passed from Jamf Pro.
orig_argv = sys.argv
# print('original_paramters: {}'.format(orig_argv))
# print("original_paramters: {}".format(orig_argv))
sys.argv = []
# sys.argv.append(orig_argv[0])
@@ -168,22 +188,22 @@ def main():
if len(arg) != 0:
sys.argv.extend(arg.split(" ", 1))
print('paramters: {}\n'.format(sys.argv))
print("paramters: {}\n".format(sys.argv))
##################################################
# Define Script Parameters
parser = argparse.ArgumentParser(description="This script installs a Bomgar Jump Client with the passed parameters.")
parser.add_argument('--key', '-k', help='Sets the Jump Client Key.', required=True)
parser.add_argument('--group', '-g', help='Sets the Jump Client Group. You must pass the Jump Group "code_name".', required=False)
parser.add_argument('--tag', '-t', help='Sets the Jump Client Tag.', required=False)
parser.add_argument('--name', '-a', help='Sets the Jump Client Name. default value: <Full Name>, <Username>', required=False)
parser.add_argument('--comments', '-c', help='Sets the Jump Client Comments. default value: <Friendly Model Name>, <Serial Number>', required=False)
parser.add_argument('--site', '-s', default="bomgar.company.org", help='Associates the Jump Client with the public portal which has the given hostname as a site address. default value: bomgar.company.org', required=False)
parser.add_argument('--policy-present', '-p', help='Policy that controls the permission policy during a support session if the customer is present at the console. You must pass the Policy\'s "code_name".', required=False)
# parser.add_argument('--policy-not-present', '-n', default="Policy-Unattended-Jump", help='Policy that controls the permission policy during a support session if the customer is not present at the console. You must pass the Policy's "code_name".', required=False)
parser.add_argument('--policy-not-present', '-n', help='Policy that controls the permission policy during a support session if the customer is not present at the console. You must pass the Policy\'s "code_name".', required=False)
parser.add_argument("--key", "-k", help="Sets the Jump Client Key.", required=True)
parser.add_argument("--group", "-g", help="Sets the Jump Client Group. You must pass the Jump Group \"code_name\".", required=False)
parser.add_argument("--tag", "-t", help="Sets the Jump Client Tag.", required=False)
parser.add_argument("--name", "-a", help="Sets the Jump Client Name. default value: <Full Name>, <Username>", required=False)
parser.add_argument("--comments", "-c", help="Sets the Jump Client Comments. default value: <Friendly Model Name>, <Serial Number>", required=False)
parser.add_argument("--site", "-s", default="bomgar.company.org", help="Associates the Jump Client with the public portal which has the given hostname as a site address. default value: bomgar.company.org", required=False)
parser.add_argument("--policy-present", "-p", help="Policy that controls the permission policy during a support session if the customer is present at the console. You must pass the Policy's \"code_name\".", required=False)
# parser.add_argument("--policy-not-present", "-n", default="Policy-Unattended-Jump", help="Policy that controls the permission policy during a support session if the customer is not present at the console. You must pass the Policy's \"code_name\".", required=False)
parser.add_argument("--policy-not-present", "-n", help="Policy that controls the permission policy during a support session if the customer is not present at the console. You must pass the Policy's \"code_name\".", required=False)
args, unknown = parser.parse_known_args(sys.argv)
# print("parsed_parameters: {}".format(args))
@@ -212,13 +232,13 @@ def main():
# Verify that a console user was present
if console_user:
# Get the Console Users' Full Name
# Get the Console Users" Full Name
full_name_cmd = "/usr/bin/dscl . -read \"/Users/{}\" dsAttrTypeStandard:RealName".format(
console_user)
full_name_results = execute_process(full_name_cmd)
if full_name_results['success']:
full_name = re.sub("RealName:\s+", "", full_name_results['stdout'])
if full_name_results["success"]:
full_name = re.sub("RealName:\s+", "", full_name_results["stdout"])
if "Setup User (_mbsetupuser)" != "{} ({})".format(full_name, console_user):
jumpName = "--jc-name '{} ({})'".format(full_name, console_user)
@@ -238,8 +258,10 @@ def main():
# Set the Jump Client Comments
if args.comments is None:
serial_number = get_system("serial")
model_friendly = get_model(serial_number)
system_info = get_system_info()
serial_number = system_info.get("serial_number")
model_friendly = system_info.get("model_friendly") if ( system_info.get("model_friendly")
) else system_info.get("model_id")
jumpComments = "--jc-comments '{}, {}'".format(model_friendly, serial_number)
else:
jumpComments = "--jc-comments '{}'".format(args.comments)
@@ -279,7 +301,7 @@ def main():
# Bits staged...
for a_file in os.listdir("/private/tmp"):
if re.search(r'(bomgar-scc-)[a-z0-9]+[.](dmg)', a_file):
if re.search(r"(bomgar-scc-)[a-z0-9]+[.](dmg)", a_file):
bomgar_installer = os.path.join("/private/tmp", a_file)
os.rename(bomgar_installer, "/tmp/bomgar-scc-{}.dmg".format(jumpKey))
bomgar_dmg = "/tmp/bomgar-scc-{}.dmg".format(jumpKey)
@@ -289,14 +311,14 @@ def main():
mount_point = mount(bomgar_dmg)
else:
print("ERROR: Bomgar DMG was not found at the expected location!")
print('***** install_Bomgar process: FAILED *****')
print("***** install_Bomgar process: FAILED *****")
sys.exit(1)
# print("mount_point: {}".format(mount_point))
try:
for a_file in os.listdir(mount_point):
if re.search(r'.+[.](app)', a_file):
if re.search(r".+[.](app)", a_file):
install_app = os.path.join(mount_point, a_file)
break
@@ -310,28 +332,28 @@ def main():
results = execute_process(install_cmd)
if not results['success']:
if not results["success"]:
print("ERROR: failed to install Bomgar Jump Client")
print("Results: {}".format(results['stderr']))
print("Results: {}".format(results["stderr"]))
sys.exit(2)
# print("Results: {}".format(results['stdout']))
# print("Results: {}".format(results["stdout"]))
else:
print("ERROR: Bomgar Jump Client installer was not found at the expected location!")
print('***** install_Bomgar process: FAILED *****')
print("***** install_Bomgar process: FAILED *****")
sys.exit(2)
print('***** install_Bomgar process: SUCCESS *****')
print("***** install_Bomgar process: SUCCESS *****")
finally:
unmount_cmd = "/usr/bin/hdiutil detach {}".format(mount_point)
results = execute_process(unmount_cmd)
if not results['success']:
if not results["success"]:
print("ERROR: failed to mount: {}".format(mount_point))
print(results['stdout'])
print(results['stderr'])
print(results["stdout"])
print(results["stderr"])
sys.exit(2)
if __name__ == "__main__":