Merge branch 'main' of https://github.com/munki/macadmin-scripts into munki-main

This commit is contained in:
Graham Pugh 2022-02-04 19:35:15 +01:00
commit 820b02c575
5 changed files with 17 additions and 298 deletions

View File

@ -3,7 +3,9 @@
Some scripts that might be of use to macOS admins. Might be related to Munki; Some scripts that might be of use to macOS admins. Might be related to Munki;
might not. might not.
These are only supported using Apple's Python on macOS. There is no support for running these on Windows or Linux. These are currently only supported using Apple's Python on macOS. There is no support for running these on Windows or Linux.
In macOS 12.3, Apple will be removing its Python 2.7 install. You'll need to provide your own Python to use these scripts. You may also need to install additional Python modules.
#### getmacosipsws.py #### getmacosipsws.py
@ -13,7 +15,7 @@ Quick-and-dirty tool to download the macOS IPSW files currently advertised by Ap
This script can create disk images containing macOS Installer applications available via Apple's softwareupdate catalogs. This script can create disk images containing macOS Installer applications available via Apple's softwareupdate catalogs.
Run `./installinstallmacos.py --help` to see the available options. Run `python ./installinstallmacos.py --help` to see the available options.
The tool assembles "Install macOS" applications by downloading the packages from Apple's softwareupdate servers and then installing them into a new empty disk image. The tool assembles "Install macOS" applications by downloading the packages from Apple's softwareupdate servers and then installing them into a new empty disk image.
@ -39,38 +41,8 @@ Use a compatible Mac or select a different build compatible with your current ha
##### Important note for Catalina+ ##### Important note for Catalina+
Catalina privacy protections might interfere with the operation of this tool if you run it from ~/Desktop, ~/Documents, ~/Downloads or other directories protected in Catalina. Consider using /Users/Shared (or subdirectory) as the "working space" for this tool. Catalina privacy protections might interfere with the operation of this tool if you run it from ~/Desktop, ~/Documents, ~/Downloads or other directories protected in Catalina. Consider using /Users/Shared (or subdirectory) as the "working space" for this tool.
##### October 2020 update
In late September, Apple made some changes to how their servers returned requested results. This led to `installinstallmacos.py` complaining about invalid XML and listing macOS installers with UNKNOWN build numbers, among other issues.
If you encounter these issues:
- Update to the current version of the script
- Run it at least once with the `--ignore-cache` option, or remove the content directory to remove corrupted files in the content cache.
##### Alternate implementations ##### Alternate implementations
Graham Pugh has a fork with a lot more features and bells and whistles. Check it out if your needs aren't met by this tool. Graham Pugh has a fork with a lot more features and bells and whistles. Check it out if your needs aren't met by this tool.
https://github.com/grahampugh/macadmin-scripts https://github.com/grahampugh/macadmin-scripts
-----
### Legacy scripts no longer in development or supported
#### createbootvolfromautonbi.py
(This tool has not been tested/updated since before 10.14 shipped. It may not work as expected with current versions of macOS. There are currently no plans to update it.)
A tool to make bootable disk volumes from the output of autonbi. Especially
useful to make bootable disks containing Imagr and the 'SIP-ignoring' kernel,
which allows Imagr to run scripts that affect SIP state, set UAKEL options, and
run the `startosinstall` component, all of which might otherwise require network
booting from a NetInstall-style nbi.
This provides a way to create a bootable external disk that acts like the Netboot environment used by/needed by Imagr.
This command converts the output of Imagr's `make nbi` into a bootable external USB disk:
`sudo ./createbootvolfromautonbi.py --nbi ~/Desktop/10.13.6_Imagr.nbi --volume /Volumes/ExternalDisk`
#### make_firmwareupdater_pkg.sh
This script was used to extract the firmware updaters from early High Sierra installers and make a standalone installer package that could be used to upgrade Mac firmware before installing High Sierra via imaging.
Later High Sierra installer changes have broken this script; since installing High Sierra via imaging is not recommended or supported by Apple and several other alternatives are now available, I don't plan on attempting to fix or upgrade this tool.

View File

@ -1,210 +0,0 @@
#!/usr/bin/python
# encoding: utf-8
#
# Copyright 2017 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
'''A tool to make bootable disk volumes from the output of autonbi. Especially
useful to make bootable disks containing Imagr and the 'SIP-ignoring' kernel,
which allows Imagr to run scripts that affect SIP state, set UAKEL options, and
run the `startosinstall` component, all of which might otherwise require network
booting from a NetInstall-style nbi.'''
import argparse
import os
import plistlib
import subprocess
import sys
import urlparse
# dmg helpers
def mountdmg(dmgpath):
"""
Attempts to mount the dmg at dmgpath and returns first mountpoint
"""
mountpoints = []
dmgname = os.path.basename(dmgpath)
cmd = ['/usr/bin/hdiutil', 'attach', dmgpath,
'-mountRandom', '/tmp', '-nobrowse', '-plist',
'-owners', 'on']
proc = subprocess.Popen(cmd, bufsize=-1,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(pliststr, err) = proc.communicate()
if proc.returncode:
print >> sys.stderr, 'Error: "%s" while mounting %s.' % (err, dmgname)
return None
if pliststr:
plist = plistlib.readPlistFromString(pliststr)
for entity in plist['system-entities']:
if 'mount-point' in entity:
mountpoints.append(entity['mount-point'])
return mountpoints[0]
def unmountdmg(mountpoint):
"""
Unmounts the dmg at mountpoint
"""
proc = subprocess.Popen(['/usr/bin/hdiutil', 'detach', mountpoint],
bufsize=-1, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
(dummy_output, err) = proc.communicate()
if proc.returncode:
print >> sys.stderr, 'Polite unmount failed: %s' % err
print >> sys.stderr, 'Attempting to force unmount %s' % mountpoint
# try forcing the unmount
retcode = subprocess.call(['/usr/bin/hdiutil', 'detach', mountpoint,
'-force'])
if retcode:
print >> sys.stderr, 'Failed to unmount %s' % mountpoint
def locate_basesystem_dmg(nbi):
'''Finds and returns the relative path to the BaseSystem.dmg within the
NetInstall.dmg'''
source_boot_plist = os.path.join(nbi, 'i386/com.apple.Boot.plist')
try:
boot_args = plistlib.readPlist(source_boot_plist)
except Exception, err:
print >> sys.stderr, err
sys.exit(-1)
kernel_flags = boot_args.get('Kernel Flags')
if not kernel_flags:
print >> sys.stderr, 'i386/com.apple.Boot.plist is missing Kernel Flags'
sys.exit(-1)
# kernel flags should in the form 'root-dmg=file:///path'
if not kernel_flags.startswith('root-dmg='):
print >> sys.stderr, 'Unexpected Kernel Flags: %s' % kernel_flags
sys.exit(-1)
file_url = kernel_flags[9:]
dmg_path = urlparse.unquote(urlparse.urlparse(file_url).path)
# return path minus leading slash
return dmg_path.lstrip('/')
def copy_system_version_plist(nbi, target_volume):
'''Copies System/Library/CoreServices/SystemVersion.plist from the
BaseSystem.dmg to the target volume.'''
netinstall_dmg = os.path.join(nbi, 'NetInstall.dmg')
if not os.path.exists(netinstall_dmg):
print >> sys.stderr, "Missing NetInstall.dmg from nbi folder"
sys.exit(-1)
print 'Mounting %s...' % netinstall_dmg
netinstall_mount = mountdmg(netinstall_dmg)
if not netinstall_mount:
sys.exit(-1)
basesystem_dmg = os.path.join(netinstall_mount, locate_basesystem_dmg(nbi))
print 'Mounting %s...' % basesystem_dmg
basesystem_mount = mountdmg(basesystem_dmg)
if not basesystem_mount:
unmountdmg(netinstall_mount)
sys.exit(-1)
source = os.path.join(
basesystem_mount, 'System/Library/CoreServices/SystemVersion.plist')
dest = os.path.join(
target_volume, 'System/Library/CoreServices/SystemVersion.plist')
try:
subprocess.check_call(
['/usr/bin/ditto', '-V', source, dest])
except subprocess.CalledProcessError, err:
print >> sys.stderr, err
unmountdmg(basesystem_mount)
unmountdmg(netinstall_mount)
sys.exit(-1)
unmountdmg(basesystem_mount)
unmountdmg(netinstall_mount)
def copy_boot_files(nbi, target_volume):
'''Copies some boot files, yo'''
files_to_copy = [
['NetInstall.dmg', 'NetInstall.dmg'],
['i386/PlatformSupport.plist',
'System/Library/CoreServices/PlatformSupport.plist'],
['i386/booter', 'System/Library/CoreServices/boot.efi'],
['i386/booter', 'usr/standalone/i386/boot.efi'],
['i386/x86_64/kernelcache',
'System/Library/PrelinkedKernels/prelinkedkernel']
]
for source, dest in files_to_copy:
full_source = os.path.join(nbi, source)
full_dest = os.path.join(target_volume, dest)
try:
subprocess.check_call(
['/usr/bin/ditto', '-V', full_source, full_dest])
except subprocess.CalledProcessError, err:
print >> sys.stderr, err
sys.exit(-1)
def make_boot_plist(nbi, target_volume):
'''Creates our com.apple.Boot.plist'''
source_boot_plist = os.path.join(nbi, 'i386/com.apple.Boot.plist')
try:
boot_args = plistlib.readPlist(source_boot_plist)
except Exception, err:
print >> sys.stderr, err
sys.exit(-1)
kernel_flags = boot_args.get('Kernel Flags')
if not kernel_flags:
print >> sys.stderr, 'i386/com.apple.Boot.plist is missing Kernel Flags'
sys.exit(-1)
# prepend the container-dmg path
boot_args['Kernel Flags'] = (
'container-dmg=file:///NetInstall.dmg ' + kernel_flags)
boot_plist = os.path.join(
target_volume,
'Library/Preferences/SystemConfiguration/com.apple.Boot.plist')
plist_dir = os.path.dirname(boot_plist)
if not os.path.exists(plist_dir):
os.makedirs(plist_dir)
try:
plistlib.writePlist(boot_args, boot_plist)
except Exception, err:
print >> sys.stderr, err
sys.exit(-1)
def bless(target_volume, label=None):
'''Bless the target volume'''
blessfolder = os.path.join(target_volume, 'System/Library/CoreServices')
if not label:
label = os.path.basename(target_volume)
try:
subprocess.check_call(
['/usr/sbin/bless', '--folder', blessfolder, '--label', label])
except subprocess.CalledProcessError, err:
print >> sys.stderr, err
sys.exit(-1)
def main():
'''Do the thing we were made for'''
parser = argparse.ArgumentParser()
parser.add_argument('--nbi', required=True, metavar='path_to_nbi',
help='Path to nbi folder created by autonbi.')
parser.add_argument('--volume', required=True,
metavar='path_to_disk_volume',
help='Path to disk volume.')
args = parser.parse_args()
copy_system_version_plist(args.nbi, args.volume)
copy_boot_files(args.nbi, args.volume)
make_boot_plist(args.nbi, args.volume)
bless(args.volume)
if __name__ == '__main__':
main()

View File

@ -1,7 +1,7 @@
#!/usr/bin/python #!/usr/bin/env python
# encoding: utf-8 # encoding: utf-8
# #
# Copyright 2021 Greg Neagle. # Copyright 2021-2022 Greg Neagle.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.

View File

@ -1,7 +1,7 @@
#!/usr/bin/python #!/usr/bin/env python
# encoding: utf-8 # encoding: utf-8
# #
# Copyright 2017 Greg Neagle. # Copyright 2017-2022 Greg Neagle.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -33,7 +33,6 @@ import plistlib
import shutil import shutil
import subprocess import subprocess
import sys import sys
import xattr
try: try:
# python 2 # python 2
@ -45,6 +44,15 @@ from xml.dom import minidom
from xml.parsers.expat import ExpatError from xml.parsers.expat import ExpatError
from distutils.version import LooseVersion from distutils.version import LooseVersion
try:
import xattr
except ImportError:
print(
"This tool requires the Python xattr module. "
"Perhaps run `pip install xattr` to install it."
)
sys.exit(-1)
DEFAULT_SUCATALOGS = { DEFAULT_SUCATALOGS = {
"17": ( "17": (

View File

@ -1,51 +0,0 @@
#!/bin/sh
# Based on investigations and work by Pepijn Bruienne
# Expects a single /Applications/Install macOS High Sierra*.app on disk
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
IDENTIFIER="com.foo.FirmwareUpdateStandalone"
VERSION=1.0
# find the Install macOS High Sierra.app and mount the embedded InstallESD disk image
echo "Mounting High Sierra ESD disk image..."
/usr/bin/hdiutil mount /Applications/Install\ macOS\ High\ Sierra*.app/Contents/SharedSupport/InstallESD.dmg
# expand the FirmwareUpdate.pkg so we can copy resources from it
echo "Expanding FirmwareUpdate.pkg"
/usr/sbin/pkgutil --expand /Volumes/InstallESD/Packages/FirmwareUpdate.pkg /tmp/FirmwareUpdate
# we don't need the disk image any more
echo "Ejecting disk image..."
/usr/bin/hdiutil eject /Volumes/InstallESD
# make a place to stage our pkg resources
/bin/mkdir -p /tmp/FirmwareUpdateStandalone/scripts
# copy the needed resources
echo "Copying package resources..."
/bin/cp /tmp/FirmwareUpdate/Scripts/postinstall_actions/update /tmp/FirmwareUpdateStandalone/scripts/postinstall
# add an exit 0 at the end of the script
echo "" >> /tmp/FirmwareUpdateStandalone/scripts/postinstall
echo "" >> /tmp/FirmwareUpdateStandalone/scripts/postinstall
echo "exit 0" >> /tmp/FirmwareUpdateStandalone/scripts/postinstall
/bin/cp -R /tmp/FirmwareUpdate/Scripts/Tools /tmp/FirmwareUpdateStandalone/scripts/
# build the package
echo "Building standalone package..."
/usr/bin/pkgbuild --nopayload --scripts /tmp/FirmwareUpdateStandalone/scripts --identifier "$IDENTIFIER" --version "$VERSION" /tmp/FirmwareUpdateStandalone/FirmwareUpdateStandalone.pkg
# clean up
/bin/rm -r /tmp/FirmwareUpdate
/bin/rm -r /tmp/FirmwareUpdateStandalone/scripts