├── AutoPkgReviewAndRun.py ├── CheckAppLastOpened.sh ├── CheckUserFolderCreationTime.sh ├── ChromeDefaultBrowser.sh ├── ChromeHomePage.mobileconfig ├── ClearLocalUserFolders ├── postinstall_script.py └── preinstall_script_munki.sh ├── ConvertKeynotesToPowerPoints.scpt ├── CreateUniversalAppIcons.py ├── CreateUniversalAppIcons.sh ├── DEPNotifyMunkiSample.sh ├── DisableiCloudSysPrefs.mobileconfig ├── DisplayAppArchitectures.py ├── DockutilAddRemoveArrays.sh ├── ExtractCombineMOVtoMP3.py ├── ExtractPrinterIPsFromMunkiPkgs.py ├── FileVaultCheck.sh ├── FixUserFolderOwnership.sh ├── KeepOnlyOneSSID.sh ├── LICENSE ├── ListCryptoUsers.py ├── MalwareRemovalScript.sh ├── OutsetLoginOnceNewUsersOnly.sh ├── OutsetPrivilegedSantaRules.sh ├── PrintersErrorPolicyAbortJob.sh ├── README.md ├── Reload_LaunchDaemons.sh ├── RemoveLastUserKeychains ├── RemoveProprietaryAppsAndMunki.sh ├── RemoveTimeMachineMenuBar.py ├── SafariEnablePlugins.mobileconfig ├── SafariEnablePlugins.sh ├── SafariHomePage.mobileconfig ├── SantaSettings.mobileconfig ├── ShoreTelPrefs.mobileconfig ├── ShowBatteryPercent.mobileconfig ├── TextWranglerSuppressUpgradePrompt.mobileconfig ├── TimeMachinePruning.sh ├── UniversalAccessDisplayContrast.mobileconfig ├── UpdateOfficeDockIcons.sh └── WiredAirDrop.mobileconfig /AutoPkgReviewAndRun.py: -------------------------------------------------------------------------------- 1 | #!/Library/AutoPkg/Python3/Python.framework/Versions/Current/bin/python3 2 | 3 | ### This script updates AutoPkg repos, verifies trust info (with prompt to update after 4 | ### review), and runs recipes in a recipe list 5 | 6 | import argparse 7 | import os 8 | import subprocess 9 | import sys 10 | 11 | # Where is the recipe list (one recipe per line) located? 12 | # Recipe list should be one recipe per line, separated by a carriage return ("\n") 13 | recipe_locations = [ os.path.expanduser('~/Library/AutoPkg/recipe_list.txt'), 14 | os.path.expanduser('~/Library/Application Support/AutoPkgr/recipe_list.txt') ] 15 | 16 | # Acceptable affirmative responses 17 | affirmative_responses = ["y", "yes", "sure", "definitely"] 18 | 19 | def get_options(): 20 | parser = argparse.ArgumentParser(description="Verifies and runs recipes in a recipe \ 21 | list. Either a specified recipe list or one in ~/Library/AutoPkg/recipe_list.txt \ 22 | or in ~/Library/Application Support/AutoPkgr/recipe_list.txt") 23 | parser.add_argument('--verifyonly', '--reviewonly', help="Only verify/review the recipes. Do not run AutoPkg.", 24 | action="store_true") 25 | parser.add_argument('--runonly', help="Only run the recipe list. Do not verify trust \ 26 | info.", action="store_true") 27 | parser.add_argument('--recipe-list', '-l', help="Path to recipe list file. If not specified, \ 28 | the default location of ~/Library/AutoPkg/recipe_list.txt will be used instead") 29 | args = parser.parse_args() 30 | # If it's verify only and run only, give an error message 31 | if args.verifyonly and args.runonly: 32 | print("The verify only and run only options are mutually exclusive.") 33 | sys.exit(1) 34 | # If it's verify only, don't run 35 | elif args.verifyonly: 36 | recipeverify = True 37 | reciperun = False 38 | # If it's run only, don't verify 39 | elif args.runonly: 40 | recipeverify = False 41 | reciperun = True 42 | # If it's neither verify only or run only, do both 43 | else: 44 | recipeverify = True 45 | reciperun = True 46 | # See if there's a recipe list specified 47 | if args.recipe_list: 48 | recipelist = args.recipe_list 49 | else: 50 | recipelist = False 51 | return recipeverify, reciperun, recipelist 52 | 53 | def get_recipe_list(recipelist, recipe_locations): 54 | # Double-check the recipe list file exists 55 | recipe_list = False 56 | if os.path.isfile(recipelist): 57 | print("Using specified {} file".format(recipelist)) 58 | recipe_list = recipelist 59 | else: 60 | print("No recipe list specified. Trying default locations...") 61 | for recipe_location in recipe_locations: 62 | if os.path.isfile(recipe_location): 63 | print("Using {}".format(recipe_location)) 64 | recipe_list = recipe_location 65 | break 66 | if recipe_list: 67 | # Put the recipes into a list 68 | try: 69 | recipes = [recipe.rstrip('\n') for recipe in open(recipe_list)] 70 | except: 71 | print("Unable to get recipe list {} into a list. Aborting run.".format(recipe_list)) 72 | sys.exit(1) 73 | return recipes 74 | else: 75 | print("Could not find a valid recipe list") 76 | sys.exit(1) 77 | 78 | def verify_recipes(recipes, affirmative_responses): 79 | # Update the repos 80 | print("Updating recipe repos...") 81 | subprocess.call([ "/usr/local/bin/autopkg", "repo-update", "all" ]) 82 | # Loop through the recipes and see which ones need to be verified 83 | for recipe in recipes: 84 | print("Verifying trust info for {}".format(recipe)) 85 | # See what the verified trust info looks like 86 | cmd = [ "/usr/local/bin/autopkg", "verify-trust-info", "-vv", recipe ] 87 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 88 | encoding='utf8') 89 | out, err = p.communicate() 90 | if err: 91 | verify_result = "Verification failure" 92 | else: 93 | verify_result = out 94 | desired_result = recipe + ": OK" 95 | if desired_result not in verify_result: 96 | print(err) 97 | confirmation = input("Do you trust these changes? (y/n) ") 98 | if confirmation.lower().strip() in affirmative_responses: 99 | print("Updating trust info for {}".format(recipe)) 100 | cmd = [ "/usr/local/bin/autopkg", "update-trust-info", recipe ] 101 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 102 | encoding='utf8') 103 | out, err = p.communicate() 104 | if err: 105 | print("Unable to update trust info: {}".format(err)) 106 | else: 107 | print("Okay. Not updating trust for {}".format(recipe)) 108 | # Remove it from the list of recipes to run... no point in running it if 109 | # the trust info isn't good 110 | recipes.remove(recipe) 111 | else: 112 | print(verify_result) 113 | return recipes 114 | 115 | def run_recipes(recipes): 116 | print("Running recipes...") 117 | cmd = [ "/usr/local/bin/autopkg", "run" ] 118 | cmd.extend(recipes) 119 | try: 120 | subprocess.call(cmd) 121 | except: 122 | print("Unable to run recipes. Aborting run.") 123 | sys.exit(1) 124 | 125 | def main(): 126 | # Get arguments 127 | recipeverify, reciperun, recipelist = get_options() 128 | 129 | # Get recipe list 130 | recipes = get_recipe_list(recipelist, recipe_locations) 131 | 132 | if recipeverify: 133 | # Get verified recipes 134 | recipes = verify_recipes(recipes, affirmative_responses) 135 | 136 | if reciperun: 137 | # Run the recipes 138 | run_recipes(recipes) 139 | 140 | if __name__ == "__main__": 141 | main() 142 | -------------------------------------------------------------------------------- /CheckAppLastOpened.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ##### User-defined input ###### 4 | # Fill in the hours ago you want to place to the threshold. Default is 30 days or 720 hours 5 | threshold=720 6 | 7 | # Application to check last opening of 8 | appToCheck='/Applications/Automator.app/' 9 | 10 | ##### End user-defined input ###### 11 | 12 | # Check the last date and time that the app was opened 13 | lastOpened=$(/usr/bin/mdls "$appToCheck" | /usr/bin/grep kMDItemLastUsedDate | /usr/bin/awk -F "= " '{print $2}') 14 | 15 | # Convert that to a UNIX Timestamp / Epoch 16 | lastOpenedTimestamp=$(/bin/date -j -f '%Y-%m-%d %H:%M:%S +0000' "$lastOpened" +%s) 17 | 18 | # Get the timestamp of the threshold 19 | thresholdTimestamp=$(/bin/date -j -v -"$threshold"H +%s) 20 | 21 | # Compare the two 22 | if [ "$lastOpenedTimestamp" -lt "$thresholdTimestamp" ]; then 23 | 24 | # If it hasn't been opened since before the threshold date, say so or do something 25 | /bin/echo "$appToCheck hasn't been opened since $lastOpened" 26 | 27 | # By default, this just echoes feedback, but you may want to perform an action... put the action here 28 | 29 | else 30 | 31 | # If it has been opened since the threshold date, say so or do something 32 | /bin/echo "$appToCheck was recently opened at $lastOpened" 33 | 34 | # By default, this just echoes feedback, but you may want to perform an action... put the action here 35 | 36 | # End comparing the two timestamps 37 | fi 38 | -------------------------------------------------------------------------------- /CheckUserFolderCreationTime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ################################################## 4 | ################################################## 5 | ## Define threshold in minutes (must be an integer--recommended less than 700 minutes) 6 | threshold=2 7 | 8 | ################################################## 9 | ################################################## 10 | 11 | # Check that the threshold is in an integer, since it's user-defined 12 | if [[ "$threshold" =~ ^[0-9]+$ ]];then 13 | echo -e "\nThreshold is $threshold minutes ago\n" 14 | 15 | # Get the current user's home directory 16 | currentUser=$(/bin/ls -l /dev/console | /usr/bin/awk '/ / { print $3 }') 17 | /bin/echo -e "Current user is $currentUser\n" 18 | 19 | # Get path of the user's Dock 20 | # By default (unless the sys admin has modified the User Template, in which case, why even have a login-once script?) 21 | # there is no com.apple.dock.plist, so we're checking to see if it doesn't exist (good) or if it's been created only within 22 | # the threshold period (also good) 23 | currentUserDock="/Users/$currentUser/Library/Preferences/com.apple.dock.plist" 24 | 25 | # Set up a test variable, since there are two instances that yield a positive result 26 | recentlyCreated=0 27 | 28 | if [ -f "$currentUserDock" ]; then 29 | 30 | /bin/echo -e "User Dock exists\n" 31 | 32 | # Get the creation date (in UNIX timestamp) of the current user dock 33 | dockCreationTimestamp=$(/usr/bin/stat -f%B "$currentUserDock") 34 | /bin/echo -e "Dock was created $dockCreationTimestamp\n" 35 | 36 | # Get today's date from [threshold] minutes ago into a UNIX timestamp 37 | thresholdTimestamp=$(/bin/date -j -v -"$threshold"M +%s) 38 | /bin/echo -e "$threshold minutes ago is $thresholdTimestamp\n" 39 | 40 | # Check to see if the folder was created in the last [threshold] minutes 41 | if [ "$dockCreationTimestamp" -gt "$thresholdTimestamp" ]; then 42 | recentlyCreated=1 43 | /bin/echo -e "The user Dock was created within the last $threshold minutes\n" 44 | 45 | else 46 | /bin/echo -e "The user Dock was created more than $threshold minutes ago\n" 47 | 48 | fi 49 | 50 | else 51 | recentlyCreated=1 52 | /bin/echo -e "There is no user Dock (which means the user just logged in and this script somehow ran before the Dock could be created)\n" 53 | 54 | # End checking the user dock exists 55 | fi 56 | 57 | if [ "$recentlyCreated" == 1 ]; then 58 | 59 | # This is where your code would go 60 | /bin/echo -e "The use Dock is either non-existent or only recently created, so you can probably run your script.\n" 61 | fi 62 | 63 | else 64 | /bin/echo -e "\nThe threshold is not an integer\n" 65 | 66 | # End checking whether the threshold is an integer or not 67 | fi 68 | -------------------------------------------------------------------------------- /ChromeDefaultBrowser.sh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | ## Note (6 Dec., 2019): The old version that used lsregister no longer worked as of 4 | ## Catalina (10.15). The version that doesn't use lsregister does appear to work in 5 | ## Ventura (13.4.1), as long as you reboot. Otherwise, trying to run lsregister afterwards 6 | ## may prevent System Settings from launching up 7 | 8 | # Desired default browser string 9 | DefaultBrowser='com.google.chrome' 10 | #DefaultBrowser='com.microsoft.edgemac' 11 | #DefaultBrowser='org.mozilla.firefox' 12 | #DefaultBrowser='com.apple.safari' 13 | 14 | # PlistBuddy executable 15 | PlistBuddy='/usr/libexec/PlistBuddy' 16 | 17 | # Plist directory 18 | PlistDirectory="$HOME/Library/Preferences/com.apple.LaunchServices" 19 | 20 | # Plist name 21 | PlistName="com.apple.launchservices.secure.plist" 22 | 23 | # Plist location 24 | PlistLocation="$PlistDirectory/$PlistName" 25 | 26 | # Array of preferences to add 27 | PrefsToAdd=("{ LSHandlerContentType = \"public.url\"; LSHandlerPreferredVersions = { LSHandlerRoleViewer = \"-\"; }; LSHandlerRoleViewer = \"$DefaultBrowser\"; }" 28 | "{ LSHandlerContentType = \"public.html\"; LSHandlerPreferredVersions = { LSHandlerRoleAll = \"-\"; }; LSHandlerRoleAll = \"$DefaultBrowser\"; }" 29 | "{ LSHandlerPreferredVersions = { LSHandlerRoleAll = \"-\"; }; LSHandlerRoleAll = \"$DefaultBrowser\"; LSHandlerURLScheme = https; }" 30 | "{ LSHandlerPreferredVersions = { LSHandlerRoleAll = \"-\"; }; LSHandlerRoleAll = \"$DefaultBrowser\"; LSHandlerURLScheme = http; }" 31 | ) 32 | 33 | # Double-check the PlistLocation exists 34 | if [[ -f "$PlistLocation" ]]; then 35 | 36 | # Initialize counter that will just keep moving us through the array of dicts 37 | # A bit imprecise... would be better if we could just count the array of dicts, but we'll stop when we get to a blank one 38 | Counter=0 39 | 40 | # Initialize DictResult just so the while loop begins 41 | DictResult='PLACEHOLDER' 42 | 43 | while [[ ! -z "$DictResult" ]]; do 44 | DictResult=$("$PlistBuddy" -c "Print LSHandlers:$Counter" "$PlistLocation") 45 | 46 | # Check for existing settings 47 | if [[ "$DictResult" == *"public.url"* || "$DictResult" == *"public.html"* || "$DictResult" == *"LSHandlerURLScheme = https"* || "$DictResult" == *"LSHandlerURLScheme = http"* ]]; then 48 | # Delete the existing. We'll add new ones in later 49 | "$PlistBuddy" -c "Delete :LSHandlers:$Counter" "$PlistLocation" 50 | /bin/echo "Deleting $Counter from Plist" 51 | fi 52 | 53 | # Increase counter 54 | Counter=$((Counter+1)) 55 | 56 | # End of while loop 57 | done 58 | 59 | # Plist does not exist 60 | else 61 | # Say the Plist does not exist 62 | /bin/echo "Plist does not exist. Creating directory for it." 63 | /bin/mkdir -p "$PlistDirectory" 64 | 65 | # End checking whether Plist exists or not 66 | fi 67 | 68 | echo "Adding in prefs" 69 | for PrefToAdd in "${PrefsToAdd[@]}" 70 | do 71 | /usr/bin/defaults write "$PlistLocation" LSHandlers -array-add "$PrefToAdd" 72 | done 73 | 74 | /bin/echo "You may need to reboot for changes to take effect." 75 | -------------------------------------------------------------------------------- /ChromeHomePage.mobileconfig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PayloadIdentifier 6 | com.managedsettings.profile.chrome 7 | PayloadRemovalDisallowed 8 | 9 | PayloadScope 10 | System 11 | PayloadType 12 | Configuration 13 | PayloadUUID 14 | 2015-04-20-6-20 15 | PayloadOrganization 16 | NAMEOFYOURORGANIZATION 17 | PayloadVersion 18 | 1 19 | PayloadDisplayName 20 | Google Chrome Settings 21 | PayloadContent 22 | 23 | 24 | PayloadType 25 | com.apple.ManagedClient.preferences 26 | PayloadVersion 27 | 1 28 | PayloadIdentifier 29 | com.normandale 30 | PayloadUUID 31 | 121-qasd 32 | PayloadEnabled 33 | 34 | PayloadDisplayName 35 | Custom: (com.google.Chrome) 36 | PayloadContent 37 | 38 | com.google.Chrome 39 | 40 | Forced 41 | 42 | 43 | mcx_preference_settings 44 | 45 | HideWebStorePromo 46 | 47 | HomepageIsNewTabPage 48 | 49 | HomepageLocation 50 | https://HOMEPAGEYOUWANTTOSETFORYOURUSERS/ 51 | RestoreOnStartup 52 | 4 53 | RestoreOnStartupURLs 54 | 55 | https://HOMEPAGEYOUWANTTOSETFORYOURUSERS 56 | 57 | ShowHomeButton 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /ClearLocalUserFolders/postinstall_script.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | import subprocess 5 | 6 | # Define a list of protected users 7 | protectedUsers = ['admin', 8 | 'Guest', 9 | 'Shared'] 10 | 11 | def main(): 12 | 13 | # List the existing user directories 14 | startDir = '/Users' 15 | userDirs=os.listdir(startDir) 16 | for userDir in userDirs: 17 | userDirFull = os.path.join(startDir, userDir) 18 | if os.path.isdir(userDirFull): 19 | if userDir not in protectedUsers: 20 | # In theory shutil.rmtree should be able to recursively delete directories. There seems to be conflicting information on that. To be safe (or dangerous--depending on how you look at it), I'm going with the old standby of sudo rm -rf 21 | #print "Will delete %s" % userDirFull 22 | cmd = "/bin/rm -rf %s" % userDirFull 23 | #print cmd 24 | subprocess.call(cmd, shell=True) 25 | 26 | if __name__ == '__main__': 27 | main() 28 | -------------------------------------------------------------------------------- /ClearLocalUserFolders/preinstall_script_munki.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Make sure not user is logged in 4 | lastUser=$(/usr/bin/defaults read /Library/Preferences/com.apple.loginwindow lastUser) 5 | 6 | if [ "$lastUser" != "loggedIn" ]; then 7 | 8 | # Exit cleanly, we can proceed 9 | # /bin/echo "User not logged in" 10 | exit 0 11 | 12 | else 13 | 14 | # Exit 1, so we'll have to re-run this again 15 | # /bin/echo "User is logged in" 16 | exit 1 17 | 18 | fi 19 | -------------------------------------------------------------------------------- /ConvertKeynotesToPowerPoints.scpt: -------------------------------------------------------------------------------- 1 | -- Bad assumptions this script makes: That there are definitely Keynotes in the folder, that the folder it's 2 | -- exporting to doesn't already have PowerPoint files of the same name, and that the user is using Keynote 8.1 3 | -- or another version of Keynote that has the exact same menu options 4 | 5 | -- How to use this: 6 | -- 1. In Automator, create an application 7 | -- 2. Drag Get Folder Contents to the workflow 8 | -- 3. Drag Run AppleScript to the workflow 9 | -- 4. Paste this code into the Run AppleScript window 10 | -- 5. Save the .app somewhere you want to use it later 11 | -- 6. Go to System Preferences > Security & Privacy > Accessibility and add your saved .app 12 | -- 7. Drag a folder full of Keynotes-to-convert onto the .app icon 13 | 14 | on run {input, parameters} 15 | 16 | repeat with f in input 17 | 18 | tell application "Finder" to set fileExtension to name extension of f 19 | 20 | if fileExtension is in {"key"} then 21 | 22 | tell application "Keynote" 23 | open f 24 | activate 25 | end tell 26 | 27 | tell application "System Events" 28 | click menu item "PowerPoint…" of ((process "Keynote")'s (menu bar 1)'s ¬ 29 | (menu bar item "File")'s (menu "File")'s ¬ 30 | (menu item "Export To")'s (menu "Export To")) 31 | end tell 32 | 33 | tell application "System Events" 34 | set window_name to name of first window of (first application process whose frontmost is true) 35 | end tell 36 | 37 | tell application "System Events" 38 | -- Click the “Next…” button. 39 | delay 0.3 40 | click UI element 9 of sheet 1 of window 1 of application process "Keynote" 41 | 42 | -- Click the “Export” button. 43 | delay 0.3 44 | click UI element "Export" of sheet 1 of window 1 of application process "Keynote" 45 | end tell 46 | tell application "Keynote" 47 | activate 48 | close every document without saving 49 | end tell 50 | end if 51 | 52 | 53 | end repeat 54 | 55 | 56 | return input 57 | end run 58 | -------------------------------------------------------------------------------- /CreateUniversalAppIcons.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | from shutil import copyfile 5 | import subprocess 6 | 7 | # Name of original image (place in the same folder as this script) 8 | originalImage="originalimage.png" 9 | 10 | # Main function 11 | def main(): 12 | 13 | if os.path.isfile(originalImage): 14 | 15 | # Define sizes and names based on https://developer.apple.com/library/content/qa/qa1686/_index.html 16 | appIcons = {'512': 'iTunesArtwork.png', '1024': 'iTunesArtwork@2x.png', '120': 'Icon-60@2x.png', '180': 'Icon-60@3x.png', '76': 'Icon-76.png', '152': 'Icon-76@2x.png', '40': 'Icon-Small-40.png', '80': 'Icon-Small-40@2x.png', '120': 'Icon-Small-40@3x.png', '29': 'Icon-Small.png', '58': 'Icon-Small@2x.png', '87': 'Icon-Small@2x.png'} 17 | 18 | # Loop through sizes and names 19 | for newSize, newName in appIcons.items(): 20 | print "Creating %s with size %s x %s" % (newName, newSize, newSize) 21 | 22 | # Make a copy of the original image to modify 23 | copyfile(originalImage, newName) 24 | 25 | # Resize the new copy 26 | cmd = '/usr/bin/sips -z ' + newSize + ' ' + newSize + ' ' + newName 27 | subprocess.call(cmd, shell=True) 28 | else: 29 | print "%s is not in the same folder as this script" % originalImage 30 | 31 | if __name__ == '__main__': 32 | main() 33 | -------------------------------------------------------------------------------- /CreateUniversalAppIcons.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Name of original image (place in the same folder as this script) 4 | originalImage="originalimage.png" 5 | 6 | # Make sure the path actually exists 7 | if [ -f "$originalImage" ]; then 8 | 9 | # Define sizes and names based on https://developer.apple.com/library/content/qa/qa1686/_index.html 10 | appIcons=(['512']='iTunesArtwork.png' ['1024']='iTunesArtwork@2x.png' ['120']='Icon-60@2x.png' ['180']='Icon-60@3x.png' ['76']='Icon-76.png' ['152']='Icon-76@2x.png' ['40']='Icon-Small-40.png' ['80']='Icon-Small-40@2x.png' ['120']='Icon-Small-40@3x.png' ['29']='Icon-Small.png' ['58']='Icon-Small@2x.png' ['87']='Icon-Small@2x.png') 11 | 12 | # Loop through each of the desired sizes 13 | for newSize in "${!appIcons[@]}" 14 | do 15 | # New filename 16 | newName="${appIcons[$newSize]}" 17 | 18 | # Make a copy first, so we keep the original image 19 | /bin/cp "$originalImage" "$newName" 20 | 21 | echo -e "Resizing $newName to $newSize by $newSize\n" 22 | /usr/bin/sips -z "$newSize" "$newSize" "$newName" 23 | 24 | done 25 | 26 | else 27 | 28 | /bin/echo "$originalImage does not exist or isn't properly named. Make sure it is in the same folder as this script" 29 | 30 | fi 31 | -------------------------------------------------------------------------------- /DEPNotifyMunkiSample.sh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | # This is just a very basic sample just so show how DEPNotify might work. The script should be run as root. 4 | dep_log=/var/tmp/depnotify.log 5 | 6 | # Get the currently logged in user. 7 | currentUser=$(/usr/bin/stat -f "%Su" /dev/console) 8 | 9 | # Make sure the Munki log exists. First, say what the location is. 10 | munki_log=/Library/Managed\ Installs/Logs/ManagedSoftwareUpdate.log 11 | 12 | # If the Munki log doesn't exist, create an empty file for it to start with. 13 | if [[ ! -f $munki_log ]]; then 14 | /usr/bin/touch $munki_log 15 | fi 16 | 17 | # Since this is running as root, run the DEPNotify process as the currently logged in user instead 18 | /usr/bin/sudo -u $currentUser /usr/bin/open -a /Applications/DEPNotify.app/Contents/MacOS/DEPNotify --args -munki -fullScreen 19 | 20 | # Run a background run of Munki 21 | /usr/local/munki/managedsoftwareupdate --auto 22 | 23 | # Quit DEPNotify 24 | /bin/echo "Command: Quit" >> $dep_log 25 | -------------------------------------------------------------------------------- /DisableiCloudSysPrefs.mobileconfig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PayloadIdentifier 6 | com.apple.mdm.4e260db0-5f73-0135-3d33-0c4de9a3ce04.alacarte 7 | PayloadRemovalDisallowed 8 | 9 | PayloadScope 10 | System 11 | PayloadType 12 | Configuration 13 | PayloadUUID 14 | 4e260db0-5f73-0135-3d33-0c4de9a3ce04 15 | PayloadOrganization 16 | NAMEOFYOURORGANIZATION 17 | PayloadVersion 18 | 1 19 | PayloadDisplayName 20 | Disable iCloud System Preferences 21 | PayloadContent 22 | 23 | 24 | PayloadType 25 | com.apple.systempreferences 26 | PayloadVersion 27 | 1 28 | PayloadIdentifier 29 | com.apple.mdm.4e260db0-5f73-0135-3d33-0c4de9a3ce04.alacarte.macosxrestrictions.6f8cae20-5f73-0135-3d34-0c4de9a3ce04.systempreferences 30 | PayloadEnabled 31 | 32 | PayloadUUID 33 | 0814e8b2-da21-25c0-acc0-8a0c3caa70d0 34 | PayloadDisplayName 35 | System Preferences 36 | DisabledPreferencePanes 37 | 38 | com.apple.preferences.icloud 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /DisplayAppArchitectures.py: -------------------------------------------------------------------------------- 1 | #!/usr/local/munki/munki-python 2 | # This is just an example of a relocatable Python 3 to use. Sub in your own if necessary. 3 | 4 | ''' 5 | Displays what architectures the installed apps are and displays a summary of how many apps 6 | are each particular architecture type 7 | ''' 8 | 9 | import plistlib 10 | from subprocess import Popen, PIPE 11 | from sys import exit 12 | 13 | def main(): 14 | print("Running system_profiler command. This may take a few minutes to complete...") 15 | cmd = [ '/usr/sbin/system_profiler', 'SPApplicationsDataType', '-xml' ] 16 | p = Popen(cmd, stdout=PIPE, stderr=PIPE) 17 | out, err = p.communicate() 18 | if err: 19 | print(f'Error running {cmd}: {err}') 20 | exit(1) 21 | else: 22 | print('Converting system_profiler output to dictionary...') 23 | try: 24 | xml_contents = plistlib.loads(out) 25 | except: 26 | print('Error converting data...') 27 | exit(1) 28 | try: 29 | applications = xml_contents[0]['_items'] 30 | except: 31 | print('Error getting items from dictionary...') 32 | exit(1) 33 | print('Getting data into more digestible form...') 34 | # Dictionary to store processed data 35 | arch_kinds = {} 36 | # Loop through the applications 37 | for application in applications: 38 | arch_kind = application['arch_kind'] 39 | app_name = application['_name'] 40 | # Add the architecture kind as a dictionary key if necessary 41 | if arch_kind not in arch_kinds.keys(): 42 | arch_kinds[arch_kind] = [] 43 | arch_kinds[arch_kind].append(app_name) 44 | # Show all apps and what category they belong to 45 | for key, value in arch_kinds.items(): 46 | print(key) 47 | print(value) 48 | print("\n") 49 | # Show subtotals summary 50 | for key, value in arch_kinds.items(): 51 | value_total = len(value) 52 | if value_total == 1: 53 | app_desc = 'app' 54 | else: 55 | app_desc = 'apps' 56 | print(f'{key}: {value_total} {app_desc}') 57 | 58 | if __name__ == "__main__": 59 | main() 60 | -------------------------------------------------------------------------------- /DockutilAddRemoveArrays.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ## This allows you to specify lists of items to remove and add in arrays, and then they'll be done in bulk using a for loop 4 | ## Items to remove should be the label (usually the name of the application) 5 | ## Items to add are the full path to the item to add (usually /Applications/NAMEOFAPP.app) 6 | ## A few examples are prepopulated in the arrays, but feel free to tweak as suits the needs of your organization 7 | 8 | itemsToRemove=( 9 | "App Store" 10 | "System Preferences" 11 | ) 12 | 13 | itemsToAdd=( 14 | "/Applications/TextWrangler.app" 15 | "/Applications/Firefox.app" 16 | ) 17 | 18 | for removalItem in "${itemsToRemove[@]}" 19 | do 20 | # Check that the item is actually in the Dock 21 | inDock=$(/usr/local/bin/dockutil --list | /usr/bin/grep "$removalItem") 22 | if [ ! -z "$inDock" ]; then 23 | /usr/local/bin/dockutil --remove "$removalItem" --no-restart 24 | fi 25 | done 26 | 27 | 28 | for additionItem in "${itemsToAdd[@]}" 29 | do 30 | # Check that the item actually exists to be added to the Dock and that it isn't already in the Dock 31 | # Stripping path and extension code based on code from http://stackoverflow.com/a/2664746 32 | additionItemString=${additionItem##*/} 33 | additionItemBasename=${additionItemString%.*} 34 | inDock=$(/usr/local/bin/dockutil --list | /usr/bin/grep "additionItemBasename") 35 | if [ -e "$additionItem" ] && [ -z "$inDock" ]; then 36 | /usr/local/bin/dockutil --add "$additionItem" --no-restart 37 | fi 38 | done 39 | 40 | /usr/bin/killall Dock 41 | -------------------------------------------------------------------------------- /ExtractCombineMOVtoMP3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | import subprocess 5 | 6 | # You can get ffmpeg from https://www.ffmpeg.org/download.html 7 | # Path to ffmpeg binary 8 | ffmpeg='/usr/local/bin/ffmpeg' 9 | 10 | # Directory of movies to convert to audio 11 | source='/PATH/TO/WHERE/THE/ORIGINAL/MOVIE/FILES/ARE' 12 | 13 | # Directory to put output audio files 14 | destination='/PATH/TO/WHERE/YOU/WANT/COMBINED/MP3S/TO/GO' 15 | 16 | # Main 17 | def main(): 18 | 19 | # First, test to make sure the ffmpeg binary exists. If it doesn't, there's no point in even running any of the rest of this script 20 | if os.path.isfile(ffmpeg): 21 | 22 | # Make sure the destination directory exists to output things 23 | if os.path.isdir(destination): 24 | 25 | # Loop through the pkgsinfo 26 | if os.path.isdir(source): 27 | for root, dirs, files in os.walk(source): 28 | for dir in dirs: 29 | # Skip directories starting with a period 30 | if dir.startswith("."): 31 | dirs.remove(dir) 32 | fulldir = os.path.join(root, dir) 33 | print "We are now in %s" % dir 34 | # Make a temporary directory to store converted files 35 | tempdir=os.path.join(destination, 'tempfiles', dir) 36 | if not os.path.isdir(tempdir): 37 | os.makedirs(tempdir) 38 | # When the os.walk happens for files, it doesn't necessarily sort the files, so we'll put them into a list to sort and go through later 39 | mp3s=[] 40 | for subroot, subdirs, subfiles in os.walk(fulldir): 41 | for file in subfiles: 42 | # Skip files that start with a period 43 | if file.startswith("."): 44 | continue 45 | # Get the full path to the file 46 | fullfile = os.path.join(subroot, file) 47 | # Get basename without the file extension 48 | filebase=os.path.splitext(file)[0] 49 | # Create path to output .mp3 50 | newmp3=os.path.join(tempdir, filebase) 51 | newmp3+='.mp3' 52 | # Convert this movie file to .mp3 53 | cmd = [ ffmpeg, '-y', '-i', fullfile, '-f', 'mp3', '-ab', '192000', '-vn', newmp3 ] 54 | print "Creating %s" % newmp3 55 | proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 56 | # Append the full path of the file to the list 57 | mp3s.append(newmp3) 58 | # Wait a second for the command to complete 59 | # time.sleep(1) 60 | proc.wait() 61 | # Path to concat text file 62 | concat=os.path.join(destination, 'tempfiles', dir) 63 | concat+='.txt' 64 | # Open file to write names of mp3s to concat 65 | f=open(concat, "w+") 66 | # Sort the list 67 | mp3s.sort() 68 | # Loop through the list and add those paths to the text file for concat 69 | for mp3 in mp3s: 70 | f.write("file '" + mp3 + "'\n") 71 | # Close text file 72 | f.close() 73 | # Name for combined mp3 74 | outputmp3=os.path.join(destination, dir) 75 | outputmp3+='.mp3' 76 | 77 | # Combine all the mp3s 78 | print "Combining %s" % concat 79 | cmd = [ ffmpeg, '-y', '-f', 'concat', '-safe', '0', '-i', concat, '-c', 'copy', outputmp3 ] 80 | print "Creating %s" % outputmp3 81 | proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 82 | # Wait a few seconds for the command to complete 83 | #time.sleep(4) 84 | proc.wait() 85 | else: 86 | print "%s is not a valid path to find files to convert." % source 87 | else: 88 | print "%s is not a valid destination directory." % destination 89 | else: 90 | print "The path %s to ffmpeg is invalid, so the script can't be run." % ffmpeg 91 | 92 | if __name__ == '__main__': 93 | main() 94 | -------------------------------------------------------------------------------- /ExtractPrinterIPsFromMunkiPkgs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | 5 | # MunkiPkg directory 6 | munkipkgdir='/PATH/TO/munkipkgs' 7 | 8 | # Prefixes of Printers 9 | printer_prefixes=['Printer', 'Copier'] 10 | 11 | # Main 12 | def main(): 13 | 14 | # Make sure the munkipkg directory exists 15 | if os.path.isdir(munkipkgdir): 16 | 17 | # Create an empty dictionary to store results 18 | printer_ip_list={} 19 | 20 | # Loop through the folders in munkipkgdir 21 | for root, dirs, files in os.walk(munkipkgdir): 22 | for dir in dirs: 23 | # Look for only the directories that start with the printer prefixes 24 | if dir.startswith(tuple(printer_prefixes)): 25 | fulldir = os.path.join(root, dir) 26 | #print "We are now in %s" % dir 27 | for subroot, subdirs, subfiles in os.walk(fulldir): 28 | for file in subfiles: 29 | if file == 'postinstall': 30 | # Get the full path to the file 31 | fullfile = os.path.join(subroot, file) 32 | #print "Full file is %s" % fullfile 33 | for line in open(fullfile): 34 | if "'-v', '" in line: 35 | printer_ip=line.replace("'-v', '", "").replace("',", "").replace("\n","").strip() 36 | elif "'-p', '" in line: 37 | printer_name=line.replace("'-p', '", "").replace("',", "").replace("\n","").strip() 38 | if printer_ip and printer_name: 39 | printer_ip_list[printer_name]=printer_ip 40 | for printer in sorted(printer_ip_list): 41 | print "%s - %s" % (printer,printer_ip_list[printer]) 42 | else: 43 | print "%s is not a valid directory." % munkipkgdir 44 | 45 | if __name__ == '__main__': 46 | main() 47 | -------------------------------------------------------------------------------- /FileVaultCheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ## Script that runs at login screen to check if FileVault encryption is enabled. If not, it adds a login script to launch up FileVault in System Preferences 4 | # Requires Outset: https://github.com/chilcote/outset/ 5 | # Requires Offset: https://github.com/aysiu/offset 6 | 7 | # Define name of openfilevault script 8 | scriptName='openfilevault.sh' 9 | 10 | # Define location of outset login directory 11 | outsetDir='/usr/local/outset/login-every/' 12 | 13 | # Check the FileVault Status 14 | filevaultOff=$(/usr/bin/fdesetup status | /usr/bin/grep "FileVault is Off") 15 | 16 | # If it's not on (or even in progress), then 17 | if [ ! -z "$filevaultOff" ]; then 18 | 19 | # Make sure the outset login directory exists but the script doesn't already exist 20 | if [ -d "$outsetDir" ] && [ ! -f "$outsetDir"/"$scriptName" ]; then 21 | 22 | # Create an outset login script 23 | /bin/echo '#!/bin/bash' >> "$outsetDir"/"$scriptName" 24 | /bin/echo '/usr/bin/osascript <<-EOF' >> "$outsetDir"/"$scriptName" 25 | /bin/echo 'tell application "System Preferences"' >> "$outsetDir"/"$scriptName" 26 | /bin/echo 'activate' >> "$outsetDir"/"$scriptName" 27 | /bin/echo 'reveal anchor "FDE" of pane id "com.apple.preference.security"' >> "$outsetDir"/"$scriptName" 28 | /bin/echo 'end tell' >> "$outsetDir"/"$scriptName" 29 | /bin/echo 'EOF' >> "$outsetDir"/"$scriptName" 30 | # Make sure the permissions are correct 31 | /bin/chmod 755 "$outsetDir"/"$scriptName" 32 | 33 | fi 34 | 35 | else 36 | # If it's on, delete the script 37 | if [ -f "$outsetDir"/"$scriptName" ]; then 38 | /bin/rm "$outsetDir"/"$scriptName" 39 | fi 40 | 41 | fi 42 | -------------------------------------------------------------------------------- /FixUserFolderOwnership.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ## Just in case you're copying folders over as root and the ownership of folders changes, this script will change the ownership back, 4 | ## assuming that the short name is the same as the user folder name, which it usually is 5 | 6 | # Get list of active users 7 | # From https://www.jamf.com/jamf-nation/discussions/3736/dscl-command-to-list-local-users-but-exclude-system-accounts#responseChild17416 8 | USER_LIST=$(/usr/bin/dscl /Local/Default -list /Users uid | /usr/bin/awk '$2 >= 100 && $0 !~ /^_/ { print $1 }') 9 | 10 | # Loop through list of users 11 | for THIS_USER in $USER_LIST; do 12 | 13 | # Determine the user home directory 14 | USER_HOME=$(/usr/bin/dscl . -read "/Users/$THIS_USER" NFSHomeDirectory | /usr/bin/awk '{print $2}') 15 | 16 | # Change ownership back to the original owner 17 | /usr/sbin/chown -R "$THIS_USER" "$USER_HOME" 18 | 19 | # End looping through list of users 20 | done 21 | -------------------------------------------------------------------------------- /KeepOnlyOneSSID.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Define the SSID to keep 4 | permanentSSID='NAMEOFSSIDYOUWANTTOKEEP' 5 | 6 | # Find the wireless network hardware port name 7 | # Code to get next line after "Wi-Fi" is from http://superuser.com/a/298125 8 | # Code to get only the text after "Device: " is from http://unix.stackexchange.com/a/24151 9 | wifiport=$(/usr/sbin/networksetup -listallhardwareports | /usr/bin/grep -A1 "Wi-Fi" | /usr/bin/sed -n -e 's/^.*Device: //p') 10 | 11 | # If it's not empty... 12 | if [ ! -z "$wifiport" ]; then 13 | 14 | # Find all the preferred networks 15 | oldpreferred=$(/usr/sbin/networksetup -listpreferredwirelessnetworks "$wifiport") 16 | 17 | # Loop through the preferred networks 18 | while read line; do 19 | if [[ "${line}" != "Preferred networks on en0:" && "${line}" != "$permanentSSID" ]]; then 20 | # Remove it from the preferred wireless networks 21 | /usr/sbin/networksetup -removepreferredwirelessnetwork "$wifiport" "${line}" 22 | # Also delete it from the system keychain 23 | /usr/bin/security delete-generic-password -a "${line}" 24 | fi 25 | done < <(/bin/echo "$oldpreferred") 26 | 27 | # End checking for wifi interface 28 | fi 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /ListCryptoUsers.py: -------------------------------------------------------------------------------- 1 | #!/usr/local/munki/munki-python 2 | # This uses Munki's Python 3 as an example, but you can specify another Munki 3 path here 3 | 4 | from subprocess import Popen, PIPE 5 | from sys import exit 6 | 7 | def get_username(uuid): 8 | cmd = [ '/usr/bin/dscl', '.', '-search', '/Users', 'GeneratedUID', uuid ] 9 | p = Popen(cmd, stdout=PIPE, stderr=PIPE, encoding='utf8') 10 | out, err = p.communicate() 11 | if err: 12 | print(f'ERROR: {err}') 13 | exit(1) 14 | else: 15 | # Separate out the output bits 16 | out_list = out.split("\t") 17 | if(len(out_list) == 3): 18 | return(out_list[0]) 19 | else: 20 | print(f"ERROR: Couldn't determine username from {out}") 21 | exit(1) 22 | 23 | def get_cryptousers(): 24 | cmd = [ '/usr/sbin/diskutil', 'apfs', 'listCryptoUsers', '/' ] 25 | p = Popen(cmd, stdout=PIPE, stderr=PIPE, encoding='utf8') 26 | out, err = p.communicate() 27 | if err: 28 | print(f'ERROR: {err}') 29 | exit(1) 30 | else: 31 | # Initialize a list to return 32 | usernames = [] 33 | # Separate lines of output 34 | out_list = out.split("\n") 35 | # Loop through the list but keep index numbers 36 | for i, item in enumerate(out_list): 37 | # If it's a local open directory user... 38 | if 'Type: Local Open Directory User' in item: 39 | # Get the previous line, which has the UUID 40 | uuid_string = (out_list[i-1]) 41 | uuid_pieces = uuid_string.split(' ') 42 | if len(uuid_pieces) > 0: 43 | print(f'Getting username for {uuid_pieces[1]}...') 44 | username = get_username(uuid_pieces[1]) 45 | print(f'Adding {username} to list...') 46 | usernames.append(username) 47 | return usernames 48 | 49 | def main(): 50 | usernames = get_cryptousers() 51 | print(f'\nCrypto Users: {usernames}') 52 | 53 | if __name__ == "__main__": 54 | main() 55 | -------------------------------------------------------------------------------- /MalwareRemovalScript.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### Not actually intended to be primary line of defense against malware. 4 | ### Get some real endpoint protection and use MalwareBytes when you need a good clean. 5 | ### This is really a quick automated way to get low-hanging fruit. 6 | ### This can run as a Munki postflight script or an Outset boot-every or an Offset logout-every 7 | ### Functions based on code from http://stackoverflow.com/a/6364244 8 | 9 | # Define log location 10 | LogLocation='/Library/Logs/MalwareRemoval.log' 11 | 12 | # Terms to search for are just examples. Obviously, tweak as you see fit. 13 | MalwareTerms=( 14 | "zeobits" 15 | "mackeeper" 16 | "MacKeeper" 17 | "MegaBackup" 18 | ) 19 | 20 | # See what the last user was 21 | LastUser=$(/usr/bin/defaults read /Library/Preferences/com.apple.loginwindow.plist lastUserName) 22 | 23 | # Define a function that checks for a wildcard and then deletes it if it's found 24 | function CheckAndRemoveLaunchD(){ 25 | if ls "$3"/Library/"$1"/*"$2"* 1> /dev/null 2>&1; then 26 | echo "$(/bin/date) - $2 exists in $1. Deleting." >> "$LogLocation" 27 | # Delete them 28 | /bin/rm "$3"/Library/"$1"/*"$2"* 29 | else 30 | /bin/echo "$(/bin/date) - $2 does not exist in $1. Yay!" >> "$LogLocation" 31 | fi 32 | } 33 | 34 | # Define a function that checks for an application and then deletes it if it's found 35 | function CheckAndRemoveApplication(){ 36 | if [ -d "/Applications/$1" ]; then 37 | echo "$(/bin/date) - $1 exists. Deleting." >> "$LogLocation" 38 | # Delete them 39 | /bin/rm -rf /Applications/"$1" 40 | else 41 | /bin/echo "$(/bin/date) - $1 does not exist. Yay!" >> "$LogLocation" 42 | fi 43 | } 44 | 45 | # Check there is a user who last logged in 46 | if [ ! -z "$LastUser" ]; then 47 | 48 | # Define the directory to look for 49 | LastUserDir="/Users/$LastUser" 50 | 51 | # Check the directory for the user exists... a bit imprecise... technically the shortname doesn't have to match the home directory name 52 | # That's why we're checking the directory exists first 53 | if [ -d "$LastUserDir" ]; then 54 | 55 | for MalwareTerm in "${MalwareTerms[@]}" 56 | do 57 | # Remove user-specific launch agents 58 | CheckAndRemoveLaunchD "LaunchAgents" "$MalwareTerm" "$LastUserDir" 59 | done 60 | 61 | # End checking directory exists 62 | fi 63 | 64 | # End checking last logged in user exists 65 | fi 66 | 67 | ## Future possible tweak--make an array to go through in a loop instead of specifying individual values 68 | 69 | # Remove launch agents 70 | for MalwareTerm in "${MalwareTerms[@]}" 71 | do 72 | CheckAndRemoveLaunchD "LaunchAgents" "$MalwareTerm" "" 73 | done 74 | 75 | # Remove launch daemons 76 | for MalwareTerm in "${MalwareTerms[@]}" 77 | do 78 | CheckAndRemoveLaunchD "LaunchDaemons" "$MalwareTerm" "" 79 | done 80 | 81 | # Also remove /Applications folder items 82 | for MalwareTerm in "${MalwareTerms[@]}" 83 | do 84 | CheckAndRemoveApplication "$MalwareTerm.app" 85 | done 86 | -------------------------------------------------------------------------------- /OutsetLoginOnceNewUsersOnly.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # To be used in conjunction with Outset: https://github.com/chilcote/outset 4 | # Allows a login-once script to apply to only new users and not existing users 5 | 6 | # Name of Outset script 7 | OUTSET_SCRIPT='ChromeDefaultBrowser.sh' 8 | 9 | # Modified from Elliot Jordan's template for a dock outset login-once 10 | # https://macadmins.slack.com/files/elliotjordan/F43LVD8HL/preinstall.txt 11 | 12 | # Get list of active users 13 | # From https://www.jamf.com/jamf-nation/discussions/3736/dscl-command-to-list-local-users-but-exclude-system-accounts#responseChild17416 14 | USER_LIST=$(/usr/bin/dscl /Local/Default -list /Users uid | /usr/bin/awk '$2 >= 100 && $0 !~ /^_/ { print $1 }') 15 | 16 | # Loop through list of users 17 | for THIS_USER in $USER_LIST; do 18 | 19 | # Determine the user home directory 20 | USER_HOME=$(/usr/bin/dscl . -read "/Users/$THIS_USER" NFSHomeDirectory | /usr/bin/awk '{print $2}') 21 | 22 | # Define the .plist location based on the home directory 23 | ONCE_PLIST="$USER_HOME/Library/Preferences/com.github.outset.once.plist" 24 | 25 | # Change the .plist to add in a fake instance of the outset script with right now's date/time 26 | /usr/bin/defaults write "$ONCE_PLIST" "/usr/local/outset/login-once/$OUTSET_SCRIPT" -date "$(date)" 27 | 28 | # Change ownership back to the original owner 29 | /usr/sbin/chown "$THIS_USER" "$ONCE_PLIST" 30 | 31 | # change the permission back to original permissions, in case those got modified 32 | /bin/chmod 600 "$ONCE_PLIST" 33 | 34 | # End looping through list of users 35 | done 36 | -------------------------------------------------------------------------------- /OutsetPrivilegedSantaRules.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This is a login-privileged-every script for Outset that configures Santa to block (after login) certain apps, depending 4 | # on what user they are. Since all Apple software is whitelisted by default by certificate, in order to block these apps, 5 | # you have to block based on binary hash instead. Since Apple software will update over time, keeping a static list of 6 | # hashes isn't helpful. So this calculates the hash based on what versions are currently installed. 7 | 8 | # User not to block apps for 9 | safe_user='USERNOTTOBLOCKAPPSFOR' 10 | 11 | # Array of offending apps to get hashes for 12 | apps_to_block=( 13 | "/Applications/App Store.app" 14 | "/Applications/FaceTime.app" 15 | "/Applications/Mail.app" 16 | "/Applications/Maps.app" 17 | "/Applications/Messages.app" 18 | "/Applications/Safari.app" 19 | "/Applications/Siri.app" 20 | ) 21 | 22 | # Get current user 23 | current_user=$(/bin/ls -l /dev/console | /usr/bin/awk '{ print $3 }') 24 | 25 | # Loop through each of the apps to block 26 | for app_to_block in "${apps_to_block[@]}" 27 | do 28 | # Check that the path actually exists 29 | if [[ -a "$app_to_block" ]]; then 30 | # If it exists, get the offending hash 31 | offending_hash=$(/usr/local/bin/santactl fileinfo "$app_to_block" --key SHA-256) 32 | 33 | # Put in rule 34 | if [[ "$current_user" == "$safe_user" ]]; then 35 | # Allow this 36 | /usr/local/bin/santactl rule --whitelist --sha256 "$offending_hash" 37 | else 38 | # Block this 39 | /usr/local/bin/santactl rule --blacklist --sha256 "$offending_hash" 40 | fi 41 | fi 42 | done 43 | -------------------------------------------------------------------------------- /PrintersErrorPolicyAbortJob.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Changes all printers' error policies to be abort-job instead of the default (stop-printer) 4 | 5 | # Loop through all the installed printers 6 | /usr/bin/lpstat -s | /usr/bin/awk -F "device for " '{print $2}' | /usr/bin/awk -F ":" '{print $1}' | while read line; do 7 | # If the line isn't empty (there may be one empty line at the top).. 8 | if [ ! -z "$line" ]; then 9 | # Change the error policy to be abort-job 10 | /bin/echo "Changing error policy to abort job for $line" 11 | /usr/sbin/lpadmin -p "$line" -o printer-error-policy=abort-job 12 | fi 13 | done 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mac Scripts and Profiles 2 | Scripts that modify system preferences on Mac OS X, or profiles to set preferences for users. 3 | -------------------------------------------------------------------------------- /Reload_LaunchDaemons.sh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | # Sample script to re-enable and reload launch daemons 4 | 5 | # Path to Launch Daemon plists 6 | plist_path='/Library/LaunchDaemons' 7 | 8 | # This associative array looks redundant, because the labels all match the plist names, 9 | # but you may find this setup helpful in case you have launch daemon labels that don't 10 | # match their corresponding plists 11 | launchd_items=( 12 | "com.googlecode.munki.appusaged" "com.googlecode.munki.appusaged.plist" 13 | "com.googlecode.munki.authrestartd" "com.googlecode.munki.authrestartd.plist" 14 | "com.googlecode.munki.logouthelper" "com.googlecode.munki.logouthelper.plist" 15 | "com.googlecode.munki.managedsoftwareupdate-check" "com.googlecode.munki.managedsoftwareupdate-check.plist" 16 | "com.googlecode.munki.managedsoftwareupdate-install" "com.googlecode.munki.managedsoftwareupdate-install.plist" 17 | "com.googlecode.munki.managedsoftwareupdate-manualcheck" "com.googlecode.munki.managedsoftwareupdate-manualcheck.plist" 18 | ) 19 | 20 | # Get all disabled launchds 21 | # Even though we're using "print-disabled," it includes enabled, too, hence the grep 22 | disabled_launchd=$(/bin/launchctl print-disabled system | /usr/bin/grep disabled) 23 | 24 | for launchd_label launchd_plist in ${(kv)launchd_items}; do 25 | # Check to see if the launch daemon is disabled 26 | if [[ $disabled_launchd == *"$launchd_label"* ]]; then 27 | echo "$launchd_label currently disabled. Re-enabling..." 28 | /bin/launchctl enable system/$launchd_label 29 | fi 30 | # Check to see if the launch daemon is launched 31 | # If a launch daemon is loaded, there will be a "state" in the output, even if the 32 | # state is "not running." A launch daemon can, in fact, be loaded but also not running 33 | launchd_check=$(/bin/launchctl print system/$launchd_label 2>&1 | /usr/bin/grep "state") 34 | if [[ -z $launchd_check ]]; then 35 | echo "$launchd_label is not launched. Launching..." 36 | /bin/launchctl bootstrap system $plist_path/$launchd_plist 37 | else 38 | echo "$launchd_label already launched." 39 | fi 40 | done 41 | -------------------------------------------------------------------------------- /RemoveLastUserKeychains: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Get the last user 4 | lastUserName=$(/usr/bin/defaults read /Library/Preferences/com.apple.loginwindow lastUserName) 5 | 6 | # Remove the keychains for that user 7 | /bin/rm -rf /Users/"$lastUserName"/Library/Keychains/* 8 | /bin/rm -rf /Users/"$lastUserName"/Library/Keychains/.f* 9 | 10 | # Add to log file 11 | /bin/echo "$(/bin/date) - Keychain deleted for $lastUserName" >> /Library/Logs/RemoveKeychains.log 12 | -------------------------------------------------------------------------------- /RemoveProprietaryAppsAndMunki.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ## Obviously, tweak this to whatever suits your organization. The framework is there—you just have to adjust what's in the removal arrays 4 | 5 | # Remove any launch daemons for the proprietary applications 6 | LaunchDs=( 7 | "/Library/LaunchAgents/com.adobe.AAM.Updater-1.0.plist" 8 | "/Library/LaunchAgents/com.symantec.uiagent.application.plist" 9 | "/Library/LaunchDaemons/com.adobe.adobeupdatedaemon.plist" 10 | "/Library/LaunchDaemons/com.adobe.agsservice.plist" 11 | "/Library/LaunchDaemons/com.adobe.ARMDC.Communicator.plist" 12 | "/Library/LaunchDaemons/com.adobe.ARMDC.SMJobBlessHelper.plist" 13 | "/Library/LaunchDaemons/com.adobe.fpsaud.plist" 14 | "/Library/LaunchDaemons/com.microsoft.office.licensing.helper.plist" 15 | "/Library/LaunchDaemons/com.microsoft.office.licensingV2.helper.plist" 16 | "/Library/LaunchDaemons/com.symantec.liveupdate.daemon.ondemand.plist" 17 | "/Library/LaunchDaemons/com.symantec.liveupdate.daemon.plist" 18 | "/Library/LaunchDaemons/com.symantec.sharedsettings.plist" 19 | "/Library/LaunchDaemons/com.symantec.symdaemon.plist" 20 | ) 21 | 22 | for LaunchD in "${LaunchDs[@]}"; do 23 | if [ -f "$LaunchD" ]; then 24 | /bin/launchctl unload "$LaunchD" 25 | /bin/rm -f "$LaunchD" 26 | /bin/echo -e "Removing $LaunchD\n" 27 | fi 28 | done 29 | 30 | # Remove proprietary applications 31 | AppsToRemove=("/Applications/Adobe Acrobat DC" 32 | "/Applications/Adobe Application Manager" 33 | "/Applications/Adobe Creative Cloud" 34 | "/Applications/Adobe Illustrator CC 2015" 35 | "/Applications/Adobe InDesign CC 2015" 36 | "/Applications/Adobe Photoshop CC 2015" 37 | "/Applications/Microsoft Office 2011" 38 | "/Applications/Microsoft Excel.app" 39 | "/Applications/Microsoft Lync.app" 40 | "/Applications/Microsoft Messenger.app" 41 | "/Applications/Microsoft OneNote.app" 42 | "/Applications/Microsoft Outlook.app" 43 | "/Applications/Microsoft PowerPoint.app" 44 | "/Applications/Microsoft Word.app" 45 | "/Applications/Symantec Solutions" 46 | "/Applications/Utilities/Adobe Application Manager" 47 | "/Applications/Utilities/Adobe Creative Cloud" 48 | "/Applications/Utilities/Adobe Installers" 49 | ) 50 | 51 | for AppToRemove in "${AppsToRemove[@]}"; do 52 | if [ -a "$AppToRemove" ]; then 53 | # /bin/echo -e "Removing $AppToRemove\n" 54 | /bin/rm -rf "$AppToRemove" 55 | /bin/echo -e "Removing $AppToRemove\n" 56 | fi 57 | done 58 | 59 | # Receipt wildcard array 60 | RECEIPTTESTS=( 61 | "com.adobe.acrobat.AcrobatDCUpd*" 62 | "com.adobe.acrobat.DC.*" 63 | "com.adobe.armdc.app.pkg" 64 | "com.adobe.EdgeCode" 65 | "com.adobe.PDApp.AdobeApplicationManager.installer.pkg" 66 | "com.adobe.pkg.Reflow" 67 | "com.adobe.pkg.Scout" 68 | "com.microsoft.mau.all.autoupdate*" 69 | "com.microsoft.merp.all.errorreporting*" 70 | "com.microsoft.office.all.automator.pkg*" 71 | "com.microsoft.office.all.clipart_search9.pkg.*" 72 | "com.microsoft.office.all.core.pkg.*" 73 | "com.microsoft.office.all.dcc.pkg.*" 74 | "com.microsoft.office.all.dock.pkg.*" 75 | "com.microsoft.office.all.equationeditor.pkg.*" 76 | "com.microsoft.office.all.excel.pkg.*" 77 | "com.microsoft.office.all.fix_permissions.pkg.*" 78 | "com.microsoft.office.all.fonts.pkg.*" 79 | "com.microsoft.office.all.graph.pkg.*" 80 | "com.microsoft.office.all.launch.pkg.*" 81 | "com.microsoft.office.all.licensing.pkg.*" 82 | "com.microsoft.office.all.ooxml.pkg.*" 83 | "com.microsoft.office.all.outlook.pkg.*" 84 | "com.microsoft.office.all.powerpoint.pkg.*" 85 | "com.microsoft.office.all.proofing_*" 86 | "com.microsoft.office.all.quit.pkg.*" 87 | "com.microsoft.office.all.required_office.pkg.*" 88 | "com.microsoft.office.all.setupasst.pkg.*" 89 | "com.microsoft.office.all.sharepointbrowserplugin.pkg.*" 90 | "com.microsoft.office.all.slt_std.pkg.*" 91 | "com.microsoft.office.all.vb.pkg.*" 92 | "com.microsoft.office.all.word.pkg.*" 93 | "com.microsoft.office.en.automator_workflow.pkg.*" 94 | "com.microsoft.office.en.clipart.pkg.*" 95 | "com.microsoft.office.en.core_resources.pkg.*" 96 | "com.microsoft.office.en.core_themes.pkg.*" 97 | "com.microsoft.office.en.dcc_resources.pkg.*" 98 | "com.microsoft.office.en.equationeditor_resources.pkg.*" 99 | "com.microsoft.office.en.excel_resources.pkg.*" 100 | "com.microsoft.office.en.excel_templates.pkg.*" 101 | "com.microsoft.office.en.excel_webqueries.pkg.*" 102 | "com.microsoft.office.en.fonts_fontcollection.pkg.*" 103 | "com.microsoft.office.en.graph_resources.pkg.*" 104 | "com.microsoft.office.en.langregister.pkg.*" 105 | "com.microsoft.office.en.outlook_resources.pkg.*" 106 | "com.microsoft.office.en.outlook_scriptmenuitems.pkg.*" 107 | "com.microsoft.office.en.powerpoint_guidedmethods.pkg.*" 108 | "com.microsoft.office.en.powerpoint_resources.pkg.*" 109 | "com.microsoft.office.en.query.pkg.*" 110 | "com.microsoft.office.en.readme.pkg.*" 111 | "com.microsoft.office.en.required.pkg.*" 112 | "com.microsoft.office.en.setupasst_resources.pkg.*" 113 | "com.microsoft.office.en.sharepointbrowserplugin_resources.pkg.*" 114 | "com.microsoft.office.en.solver.pkg.*" 115 | "com.microsoft.office.en.sounds.pkg.*" 116 | "com.microsoft.office.en.vb_resources.pkg.*" 117 | "com.microsoft.office.en.word_resources.pkg.*" 118 | "com.microsoft.office.en.word_templates.pkg.*" 119 | "com.microsoft.office.en.word_wizards.pkg.*" 120 | "com.microsoft.package.Fonts" 121 | "com.microsoft.package.Frameworks" 122 | "com.microsoft.package.Microsoft_AutoUpdate.app" 123 | "com.microsoft.package.Microsoft_Excel.app" 124 | "com.microsoft.package.Microsoft_Outlook.app" 125 | "com.microsoft.package.Microsoft_PowerPoint.app" 126 | "com.microsoft.package.Microsoft_Word.app" 127 | "com.microsoft.package.Proofing_Tools" 128 | "com.microsoft.lync.all.lync.pkg.*" 129 | "com.microsoft.lync.all.meetingjoinplugin.pkg.*" 130 | "com.microsoft.msgr.all.messenger.pkg.*" 131 | "com.microsoft.office.en.flip4mac.pkg.*" 132 | "com.microsoft.office.en.powerpoint_templates.pkg.*" 133 | "com.microsoft.office.en.silverlight.pkg.*" 134 | "com.microsoft.pkg.licensing*" 135 | "com.symantec.*" 136 | "com.Symantec.*" 137 | ) 138 | 139 | # Remove receipts for proprietary applications 140 | for RECEIPTTEST in "${RECEIPTTESTS[@]}"; do 141 | # /bin/echo -e "Processing $RECEIPTTEST\n" 142 | RECEIPTWILD=$(pkgutil --pkgs="$RECEIPTTEST") 143 | if [ ! -z "$RECEIPTWILD" ]; then 144 | for ARECEIPT in $RECEIPTWILD; do 145 | /usr/sbin/pkgutil --forget "$ARECEIPT" 146 | /bin/echo -e "Forgetting $ARECEIPT\n" 147 | done 148 | fi 149 | done 150 | 151 | # Remove Munki (list from https://github.com/munki/munki/wiki/Removing-Munki) 152 | /bin/echo -e "Removing Munki\n" 153 | /bin/launchctl unload /Library/LaunchDaemons/com.googlecode.munki.* 154 | /bin/rm -rf "/Applications/Utilities/Managed Software Update.app" 155 | /bin/rm -rf "/Applications/Managed Software Center.app" 156 | /bin/rm -f /Library/LaunchDaemons/com.googlecode.munki.* 157 | /bin/rm -f /Library/LaunchAgents/com.googlecode.munki.* 158 | /bin/rm -rf "/Library/Managed Installs" 159 | /bin/rm -f /Library/Preferences/ManagedInstalls.plist 160 | /bin/rm -rf /usr/local/munki 161 | /bin/rm /etc/paths.d/munki 162 | /usr/sbin/pkgutil --forget com.googlecode.munki.admin 163 | /usr/sbin/pkgutil --forget com.googlecode.munki.app 164 | /usr/sbin/pkgutil --forget com.googlecode.munki.core 165 | /usr/sbin/pkgutil --forget com.googlecode.munki.launchd 166 | 167 | 168 | CrashPlanUninstall="/Library/Application Support/CrashPlan/Uninstall.app/Contents/Resources/uninstall.sh" 169 | 170 | # See if CrashPlan uninstall exists... we could call it, but the uninstall doesn't change that often, so we're going to do a copy-and-paste instead 171 | if [ -f "$CrashPlanUninstall" ]; then 172 | 173 | /bin/bash "$CrashPlanUninstall" 174 | 175 | # End checking for CrashPlan 176 | fi 177 | -------------------------------------------------------------------------------- /RemoveTimeMachineMenuBar.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import subprocess 3 | 4 | ### Rationale for this: there is a built-in shell command to add to a .plist array, but there isn't a command to remove from a .plist array. And I know there is a library that allows Python to interact directly with .plist files, but I'm not quite up to speed on that yet, so this messy workaround will do in the meantime. The approach is messy, but it should work. Basically it takes the output of reading the array, and dumps that output into a variable. Then it gets all the junk out of that variable, including the offending entry (which can appear one of two ways), and then writes the modified variable back to the array, overwriting the existing array with everything but the offending entry ### 5 | 6 | # Specify the offending entry 7 | offendingEntryOne = '"/System/Library/CoreServices/Menu Extras/TimeMachine.menu", ' 8 | 9 | # Yes, this is messy, but for the first iteration of this script anyway, we're just going to specify the other way the offending entry could appear 10 | offendingEntryTwo = '"/System/Library/CoreServices/Menu Extras/TimeMachine.menu"' 11 | 12 | # Get the current menus 13 | currentMenus = subprocess.check_output("/usr/bin/defaults read com.apple.systemuiserver menuExtras", shell=True) 14 | 15 | # Print out the current menus 16 | print "The output of the command is %s" % (currentMenus) 17 | 18 | ### This may be a bit sloppy. Not sure yet whether the following three commands can be combined into one command that gets rid of the parentheses, the carriage returns, the extra spaces, and the extra commas ### 19 | 20 | # Get rid of the parentheses and the carriage returns 21 | currentMenus = currentMenus.translate(None, '()\n') 22 | 23 | # Get rid of the extra commas and spaces 24 | currentMenus = currentMenus.replace(',", "', '", "') 25 | 26 | # Get rid of the leading space in the beginning (merely cosmetic, so commenting out for now) 27 | #currentMenus = currentMenus.replace(' ','') 28 | 29 | # Print out the current menus 30 | #print "Before taking out the offending entry, it looks like %s" % (currentMenus) 31 | 32 | # Take out the offending entry, pass one 33 | if currentMenus.find(offendingEntryOne) != -1: 34 | currentMenus = currentMenus.replace(offendingEntryOne, '') 35 | elif currentMenus.find(offendingEntryTwo) != -1: 36 | # Take out the offending entry, pass two 37 | currentMenus = currentMenus.replace(offendingEntryTwo, '') 38 | 39 | # Print out the current menus 40 | #print "After taking out the offending entry, it looks like %s" % (currentMenus) 41 | 42 | # Let's write it back and then refresh the menu bar 43 | subprocess.call("/usr/bin/defaults write com.apple.systemuiserver menuExtras -array " + currentMenus + " && /usr/bin/killall -KILL SystemUIServer", shell=True) 44 | -------------------------------------------------------------------------------- /SafariEnablePlugins.mobileconfig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PayloadContent 6 | 7 | 8 | PayloadContent 9 | 10 | com.apple.Safari 11 | 12 | Set-Once 13 | 14 | 15 | mcx_data_timestamp 16 | 2016-10-14T16:15:13Z 17 | mcx_preference_settings 18 | 19 | ManagedPlugInPolicies 20 | 21 | com.macromedia.Flash Player.plugin 22 | 23 | PlugInDisallowPromptBeforeUseDialog 24 | 25 | PlugInFirstVisitPolicy 26 | PlugInPolicyAsk 27 | 28 | com.microsoft.SilverlightPlugin 29 | 30 | PlugInDisallowPromptBeforeUseDialog 31 | 32 | PlugInFirstVisitPolicy 33 | PlugInPolicyAsk 34 | 35 | com.oracle.java.JavaAppletPlugin 36 | 37 | PlugInDisallowPromptBeforeUseDialog 38 | 39 | PlugInFirstVisitPolicy 40 | PlugInPolicyAsk 41 | 42 | 43 | PlugInInfo 44 | 45 | com.macromedia.Flash Player.plugin 46 | 47 | plugInCurrentState 48 | 49 | 50 | com.microsoft.SilverlightPlugin 51 | 52 | plugInCurrentState 53 | 54 | 55 | com.oracle.java.JavaAppletPlugin 56 | 57 | plugInCurrentState 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | PayloadEnabled 67 | 68 | PayloadIdentifier 69 | MCXToProfile.a307c7ad-4bc0-482b-8bee-f3199bcecf00.alacarte.customsettings.828ec6eb-b10a-4372-a337-6912bbff1555 70 | PayloadType 71 | com.apple.ManagedClient.preferences 72 | PayloadUUID 73 | 828ec6eb-b10a-4372-a337-6912bbff1555 74 | PayloadVersion 75 | 1 76 | 77 | 78 | PayloadDescription 79 | Enables Flash, Java, and Silverlight plugins in Safari 80 | PayloadDisplayName 81 | Safari Plugins Enabled 82 | PayloadIdentifier 83 | SafariPrefs 84 | PayloadOrganization 85 | NAMEOFYOURORGANIZATION 86 | PayloadRemovalDisallowed 87 | 88 | PayloadScope 89 | System 90 | PayloadType 91 | Configuration 92 | PayloadUUID 93 | a307c7ad-4bc0-482b-8bee-f3199bcecf00 94 | PayloadVersion 95 | 1 96 | 97 | 98 | -------------------------------------------------------------------------------- /SafariEnablePlugins.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | plugins=( 4 | "com.adobe.acrobat.pdfviewerNPAPI" 5 | "com.adobe.director.shockwave.pluginshim" 6 | "com.cisco_webex.plugin.gpc64" 7 | "com.citrix.citrixicaclientplugIn" 8 | "com.macromedia.Flash\ Player.plugin" 9 | "com.microsoft.SilverlightPlugin" 10 | "com.oracle.java.JavaAppletPlugin" 11 | ) 12 | 13 | # Loop through the plugins 14 | for plugin in "${plugins[@]}" 15 | do 16 | # Set the policy to ask instead of off 17 | /usr/libexec/PlistBuddy -c "Set :ManagedPlugInPolicies:$plugin:PlugInFirstVisitPolicy 'PlugInPolicyAsk'" ~/Library/Preferences/com.apple.Safari.plist 18 | 19 | # See if the disallow prompt before use dialogue exists 20 | current_setting=$(/usr/libexec/PlistBuddy -c "Print :ManagedPlugInPolicies:$plugin:PlugInDisallowPromptBeforeUseDialog" ~/Library/Preferences/com.apple.Safari.plist | grep -e true -e false) 21 | 22 | if [ "$current_setting" == false ]; then 23 | /usr/libexec/PlistBuddy -c "Set :ManagedPlugInPolicies:$plugin:PlugInDisallowPromptBeforeUseDialog bool true" ~/Library/Preferences/com.apple.Safari.plist 24 | 25 | elif [ "$current_setting" != true ]; then 26 | /usr/libexec/PlistBuddy -c "Add :ManagedPlugInPolicies:$plugin:PlugInDisallowPromptBeforeUseDialog bool true" ~/Library/Preferences/com.apple.Safari.plist 27 | 28 | fi 29 | done 30 | -------------------------------------------------------------------------------- /SafariHomePage.mobileconfig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PayloadContent 6 | 7 | 8 | PayloadContent 9 | 10 | com.apple.Safari 11 | 12 | Forced 13 | 14 | 15 | mcx_preference_settings 16 | 17 | AlwaysRestoreSessionAtLaunch 18 | 19 | HomePage 20 | https://HOMEPAGEYOUWANTTOSETFORYOURUSERS/ 21 | NewTabBehavior 22 | 0 23 | NewWindowBehavior 24 | 0 25 | 26 | 27 | 28 | 29 | 30 | PayloadEnabled 31 | 32 | PayloadIdentifier 33 | MCXToProfile.4cd93554-a26b-45a5-b17a-620f7dc4e8b2.alacarte.customsettings.08118259-a9d8-49ae-bea9-90b21182bf73 34 | PayloadType 35 | com.apple.ManagedClient.preferences 36 | PayloadUUID 37 | 08118259-a9d8-49ae-bea9-90b21182bf73 38 | PayloadVersion 39 | 1 40 | 41 | 42 | PayloadDescription 43 | Safari Settings 44 | PayloadDisplayName 45 | Safari Settings 46 | PayloadIdentifier 47 | SafariSettings 48 | PayloadOrganization 49 | NAMEOFYOURORGANIZATION 50 | PayloadRemovalDisallowed 51 | 52 | PayloadScope 53 | System 54 | PayloadType 55 | Configuration 56 | PayloadUUID 57 | 4cd93554-a26b-45a5-b17a-620f7dc4e8b2 58 | PayloadVersion 59 | 1 60 | 61 | 62 | -------------------------------------------------------------------------------- /SantaSettings.mobileconfig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PayloadContent 6 | 7 | 8 | PayloadContent 9 | 10 | com.google.santa 11 | 12 | Forced 13 | 14 | 15 | mcx_preference_settings 16 | 17 | ClientMode 18 | 2 19 | WhitelistRegex 20 | ^(?:/Applications/Adobe Acrobat DC)/.*|^(?:/Applications/Adobe Photoshop CC 2015)/.*|^(?:/Applications/Adobe Photoshop CC 2017)/.*|^(?:/Applications/Bitdefender)/.*|^(?:/Applications/Endpoint Security for Mac.app)/.*|^(?:/Applications/LockDown Browser.app)/.*|^(?:/Applications/Manager.app)/.*|^(?:/Applications/Managed Software Center.app)/.*|^(?:/Applications/Microsoft Excel.app)/.*|^(?:/Applications/Microsoft PowerPoint.app)/.*|^(?:/Applications/Microsoft Word.app)/.*|^(?:/Library/Application Support/Symantec)/.* 21 | 22 | 23 | 24 | 25 | 26 | PayloadEnabled 27 | 28 | PayloadIdentifier 29 | 0342c558-a101-4a08-a0b9-40cc00039ea5 30 | PayloadType 31 | com.apple.ManagedClient.preferences 32 | PayloadUUID 33 | 0342c558-a101-4a08-a0b9-40cc00039ea5 34 | PayloadVersion 35 | 1 36 | 37 | 38 | PayloadDescription 39 | Santa Settings 40 | PayloadDisplayName 41 | Santa Settings 42 | PayloadIdentifier 43 | com.google.santa 44 | PayloadOrganization 45 | NAMEOFYOURORGANIZATION 46 | PayloadRemovalDisallowed 47 | 48 | PayloadScope 49 | System 50 | PayloadType 51 | Configuration 52 | PayloadUUID 53 | 9020fb2d-cab3-420f-9268-acca4868bdd0 54 | PayloadVersion 55 | 1 56 | 57 | 58 | -------------------------------------------------------------------------------- /ShoreTelPrefs.mobileconfig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PayloadContent 6 | 7 | 8 | PayloadContent 9 | 10 | com.shoretel.ShoreTel-Communicator 11 | 12 | Set-Once 13 | 14 | 15 | mcx_data_timestamp 16 | 2016-10-14T18:08:51Z 17 | mcx_preference_settings 18 | 19 | HomeServerName 20 | FILLINYOURIPADDRESS 21 | ServerName 22 | FILLINYOURIPADDRESS 23 | 24 | 25 | 26 | 27 | 28 | PayloadEnabled 29 | 30 | PayloadIdentifier 31 | MCXToProfile.c11bc3f7-f62e-444a-a2c9-e57036a14369.alacarte.customsettings.960df557-b50e-440f-8fdf-b03ed8d077b1 32 | PayloadType 33 | com.apple.ManagedClient.preferences 34 | PayloadUUID 35 | 960df557-b50e-440f-8fdf-b03ed8d077b1 36 | PayloadVersion 37 | 1 38 | 39 | 40 | PayloadDescription 41 | Sets server name for ShoreTel Communicator 42 | PayloadDisplayName 43 | ShoreTel Communicator Settings 44 | PayloadIdentifier 45 | ShoreTelPrefs 46 | PayloadOrganization 47 | FILLINYOURORGANIZATIONNAME 48 | PayloadRemovalDisallowed 49 | 50 | PayloadScope 51 | System 52 | PayloadType 53 | Configuration 54 | PayloadUUID 55 | c11bc3f7-f62e-444a-a2c9-e57036a14369 56 | PayloadVersion 57 | 1 58 | 59 | 60 | -------------------------------------------------------------------------------- /ShowBatteryPercent.mobileconfig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PayloadContent 6 | 7 | 8 | PayloadContent 9 | 10 | com.apple.menuextra.battery 11 | 12 | Forced 13 | 14 | 15 | mcx_preference_settings 16 | 17 | ShowPercent 18 | YES 19 | 20 | 21 | 22 | 23 | 24 | PayloadEnabled 25 | 26 | PayloadIdentifier 27 | MCXToProfile.198dfeae-b81e-454e-a4e3-02e0d3844b26.alacarte.customsettings.afd02558-3a31-4b57-bb83-3a69620ceb77 28 | PayloadType 29 | com.apple.ManagedClient.preferences 30 | PayloadUUID 31 | afd02558-3a31-4b57-bb83-3a69620ceb77 32 | PayloadVersion 33 | 1 34 | 35 | 36 | PayloadDescription 37 | Shows the battery percentage in the toolbar 38 | 39 | PayloadDisplayName 40 | ShowBatteryPercent 41 | PayloadIdentifier 42 | ShowBatteryPercent 43 | PayloadOrganization 44 | NAMEOFYOURORGANIZATION 45 | PayloadRemovalDisallowed 46 | 47 | PayloadScope 48 | System 49 | PayloadType 50 | Configuration 51 | PayloadUUID 52 | 198dfeae-b81e-454e-a4e3-02e0d3844b26 53 | PayloadVersion 54 | 1 55 | 56 | 57 | -------------------------------------------------------------------------------- /TextWranglerSuppressUpgradePrompt.mobileconfig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PayloadContent 6 | 7 | 8 | PayloadContent 9 | 10 | com.barebones.textwrangler 11 | 12 | Forced 13 | 14 | 15 | mcx_preference_settings 16 | 17 | LastUpgradeAlertDate 18 | 2030-11-02T08:31:15Z 19 | 20 | 21 | 22 | 23 | 24 | PayloadEnabled 25 | 26 | PayloadIdentifier 27 | MCXToProfile.5cedf15f-4efc-4fb1-b90a-38d26be93e84.alacarte.customsettings.31a35b0d-8f9a-4367-92ab-59b4fb979d71 28 | PayloadType 29 | com.apple.ManagedClient.preferences 30 | PayloadUUID 31 | 31a35b0d-8f9a-4367-92ab-59b4fb979d71 32 | PayloadVersion 33 | 1 34 | 35 | 36 | PayloadDescription 37 | Prevents TextWrangler from constantly prompting to upgrade to BBEdit 38 | PayloadDisplayName 39 | TextWrangler Suppress Upgrade Prompt 40 | PayloadIdentifier 41 | TextWranglerSuppressUpgradePrompt 42 | PayloadOrganization 43 | NAMEOFYOURORGANIZATION/string> 44 | PayloadRemovalDisallowed 45 | 46 | PayloadScope 47 | System 48 | PayloadType 49 | Configuration 50 | PayloadUUID 51 | 5cedf15f-4efc-4fb1-b90a-38d26be93e84 52 | PayloadVersion 53 | 1 54 | 55 | 56 | -------------------------------------------------------------------------------- /TimeMachinePruning.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #################################### 4 | #### Start user-defined variables #### 5 | 6 | # Define the Time Machine Backup location 7 | backupLocation="/PATH/TO/TIMEMACHINEBACKUPS/FOLDERABOVEDATENAMEDFOLDERS" 8 | 9 | # Define how many days back you want to keep 10 | thresholdDays=60 11 | 12 | #### End user-defined variables #### 13 | #################################### 14 | 15 | # Get threshold date in timestamp form 16 | thresholdTimestamp=$(/bin/date -j -v -"$thresholdDays"d "+%s") 17 | 18 | # Change directories to Time Machine Backups 19 | /usr/bin/cd "$backupLocation" 20 | 21 | # Loop through the Time Machine Backup folders 22 | for f in *; do 23 | 24 | # Ignore "Latest" 25 | if [ "$f" != "Latest" ]; then 26 | 27 | # Get just the date part of the name 28 | testDate=${f:0:10} 29 | 30 | # Get the timestamp of the date 31 | testTimestamp=$(/bin/date -j -u -f "%Y-%m-%d" "$testDate" "+%s") 32 | 33 | # Compare it to the threshold timestamp 34 | if [ "$thresholdTimestamp" -gt "$testTimestamp" ]; then 35 | 36 | /bin/echo "Going to delete $backupLocation/$f" 37 | /usr/bin/tmutil delete "$backupLocation/$f" 38 | 39 | # End comparison of timestamps 40 | fi 41 | 42 | # End checking not Latest folder 43 | fi 44 | 45 | # End looping through folders 46 | done 47 | -------------------------------------------------------------------------------- /UniversalAccessDisplayContrast.mobileconfig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PayloadContent 6 | 7 | 8 | PayloadContent 9 | 10 | com.apple.universalaccess 11 | 12 | Forced 13 | 14 | 15 | mcx_preference_settings 16 | 17 | contrast 18 | 0.0 19 | 20 | 21 | 22 | 23 | 24 | PayloadEnabled 25 | 26 | PayloadIdentifier 27 | MCXToProfile.ed3d9723-c4a6-4b78-8557-8ab6042501a4.alacarte.customsettings.3c34e442-187a-4951-bbe1-82ed51d0f6c2 28 | PayloadType 29 | com.apple.ManagedClient.preferences 30 | PayloadUUID 31 | 3c34e442-187a-4951-bbe1-82ed51d0f6c2 32 | PayloadVersion 33 | 1 34 | 35 | 36 | PayloadDescription 37 | Universal Access Settings 38 | 39 | PayloadDisplayName 40 | Universal Access Settings 41 | PayloadIdentifier 42 | UniversalAccess 43 | PayloadOrganization 44 | NAMEOFYOURORGANIZATION 45 | PayloadRemovalDisallowed 46 | 47 | PayloadScope 48 | System 49 | PayloadType 50 | Configuration 51 | PayloadUUID 52 | ed3d9723-c4a6-4b78-8557-8ab6042501a4 53 | PayloadVersion 54 | 1 55 | 56 | 57 | -------------------------------------------------------------------------------- /UpdateOfficeDockIcons.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Wait a few seconds 4 | /bin/sleep 3 5 | 6 | # Check to see if MathType is installed. We don't want to make any changes if MathType is installed 7 | if [ ! -f "/Applications/MathType 6/MathType.app/Contents/Info.plist" ]; then 8 | 9 | # Logic 10 | # If it's not installed, let's go ahead and update stuff 11 | # 1. Old version exists, new version doesn't exist - replace 12 | # 2. Old version exists and new version exists - do nothing 13 | # 3. Old version doesn't exit and new version doesn't exist - do nothing 14 | # 4. New version in only - do nothing 15 | 16 | # Array of office apps to replace in Dock 17 | office_apps=( 18 | "Microsoft Word" 19 | "Microsoft Excel" 20 | "Microsoft PowerPoint" 21 | "Microsoft Outlook" 22 | ) 23 | 24 | # Initialize a test variable... we don't want to kill the dock unless we've made any changes 25 | changes_made=0 26 | 27 | # Loop through the list of Office apps 28 | for office_app in "${office_apps[@]}" 29 | do 30 | 31 | # Create the %20 version of the office app name 32 | no_spaces="${office_app/ /%20}" 33 | 34 | # See if the old version is in there 35 | old_version=$(/usr/local/bin/dockutil --list | /usr/bin/grep -n "Microsoft%20Office%202011/$no_spaces.app") 36 | 37 | # See if the new version is in there 38 | new_version=$(/usr/local/bin/dockutil --list | /usr/bin/grep -n "/Applications/$no_spaces.app") 39 | 40 | # If there is an old version but no new version... 41 | if [ ! -z "$old_version" ] && [ -z "$new_version" ]; then 42 | 43 | # Update the old version to the new one 44 | /bin/echo "Replacing 2011 version of $office_app" 45 | /usr/local/bin/dockutil --add /Applications/"$office_app".app --replacing "$office_app" --no-restart 46 | 47 | # If the changes made is still 0, make it 1 48 | if [ "$changes_made" == 0 ]; then 49 | changes_made=1 50 | # End checking if no changes had been made before 51 | fi 52 | # End checking if the old version of the Office app is there but not the new version 53 | fi 54 | 55 | # End looping through the Office apps 56 | done 57 | 58 | # If there were changes made, kill the Dock 59 | if [ "$changes_made" == 1 ]; then 60 | /usr/bin/killall Dock 61 | # End checking changes have been made 62 | fi 63 | 64 | # End checking MathType isn't installed 65 | fi 66 | -------------------------------------------------------------------------------- /WiredAirDrop.mobileconfig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PayloadContent 6 | 7 | 8 | PayloadContent 9 | 10 | com.apple.NetworkBrowser 11 | 12 | Forced 13 | 14 | 15 | mcx_data_timestamp 16 | 2015-11-09T23:15:59Z 17 | mcx_preference_settings 18 | 19 | BrowseAllInterfaces 20 | 1 21 | 22 | 23 | 24 | 25 | 26 | PayloadEnabled 27 | 28 | PayloadIdentifier 29 | MCXToProfile.c6b28487-f935-4264-8451-55758b0bea5f.alacarte.customsettings.b90da586-a479-41e5-bd10-9c5b4a47450d 30 | PayloadType 31 | com.apple.ManagedClient.preferences 32 | PayloadUUID 33 | b90da586-a479-41e5-bd10-9c5b4a47450d 34 | PayloadVersion 35 | 1 36 | 37 | 38 | PayloadDescription 39 | Sets it up so AirDrop will work on even wired connections 40 | PayloadDisplayName 41 | Wired AirDrop 42 | PayloadIdentifier 43 | WiredAirDrop 44 | PayloadOrganization 45 | NAMEOFYOURORGANIZATION 46 | PayloadRemovalDisallowed 47 | 48 | PayloadScope 49 | System 50 | PayloadType 51 | Configuration 52 | PayloadUUID 53 | c6b28487-f935-4264-8451-55758b0bea5f 54 | PayloadVersion 55 | 1 56 | 57 | 58 | --------------------------------------------------------------------------------