├── jamfMigrator.png ├── FV2 Reissue Script.png ├── Create FV_enabled User.png ├── com.github.mlbz521.jamfMigrator.plist ├── LICENSE ├── postinstall.sh ├── README.md └── jamfMigrator.sh /jamfMigrator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLBZ521/jamfMigrator/HEAD/jamfMigrator.png -------------------------------------------------------------------------------- /FV2 Reissue Script.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLBZ521/jamfMigrator/HEAD/FV2 Reissue Script.png -------------------------------------------------------------------------------- /Create FV_enabled User.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLBZ521/jamfMigrator/HEAD/Create FV_enabled User.png -------------------------------------------------------------------------------- /com.github.mlbz521.jamfMigrator.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | com.github.mlbz521.jamfMigrator 7 | ProgramArguments 8 | 9 | /bin/bash 10 | -c 11 | (/private/var/tmp/jamfMigrator.sh) 12 | 13 | RunAtLoad 14 | 15 | StartInterval 16 | 600 17 | AbandonProcessGroup 18 | 19 | 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Zack T 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /postinstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ################################################################################################### 4 | # Script Name: postinstall.sh 5 | # By: Zack Thompson / Created: 11/20/2017 6 | # Version: 1.0 / Updated: 11/21/2017 / By: ZT 7 | # 8 | # Description: This script stages files and loads a LaunchDaemon. 9 | # 10 | ################################################################################################### 11 | 12 | # Define the Variables 13 | pkgDir=$(/usr/bin/dirname $0) 14 | launchDaemonLabel="com.github.mlbz521.jamfMigrator" 15 | osVersion=$(sw_vers -productVersion | /usr/bin/awk -F '.' '{print $2}') 16 | launchDaemonLocation="/Library/LaunchDaemons/${launchDaemonLabel}.plist" 17 | 18 | # Stages the bits 19 | cp "${pkgDir}/${launchDaemonLabel}.plist" $launchDaemonLocation 20 | cp -R "${pkgDir}/jamfMigrator.sh" /private/var/tmp/ 21 | cp -R "${pkgDir}/QuickAdd.pkg" /private/var/tmp/ 22 | 23 | # Determine proper launchctl syntax based on OS Version 24 | if [[ ${osVersion} -ge 11 ]]; then 25 | /bin/launchctl bootstrap system $launchDaemonLocation 26 | /bin/launchctl enable system/$launchDaemonLabel 27 | elif [[ ${osVersion} -le 10 ]]; then 28 | /bin/launchctl load $launchDaemonLocation 29 | fi 30 | 31 | exit 0 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jamfMigrator 2 | This project assists with migrating from one JSS to another JSS. I will describe the setup process and logic of the script below. 3 | 4 | I've added an additional workflow to this project to include some kind of option for 'migrating' FileVault keys to the new JSS. 5 | 6 | The overall scope of this project is to: 7 | * Mark the computer in the old Jamf environment as “unmanaged” – so you can easily track what has, and has not, migrated 8 | * Remove the old Jamf Framework – which removes the main MDM Profile and all traces of Jamf related Config Profiles, MDM certs, etc 9 | * Join computer to the new Jamf environment in a clean state 10 | * Reissue a new FileVault Personal Recovery Key (requires a known FileVault Unlock Key) 11 | 12 | In my testing, the overall process took, from policy execution to the script completing, roughly one minute and fifteen seconds. 13 | 14 | **Inspired by several discussions on JamfNation:** 15 | * https://www.jamf.com/jamf-nation/discussions/10866/un-manage-and-keep-in-inventory 16 | * https://www.jamf.com/jamf-nation/discussions/10456/remove-framework-after-imaging 17 | * And several other threads I've read regarding Jamf managed state recovery methods (See: rtrouton's CasperCheck) 18 | 19 | #### WorkFlow #### 20 | 21 | ![Flow Chart](https://github.com/MLBZ521/jamfMigrator/blob/master/jamfMigrator.png "JamfMigrator Flow Chart") 22 | 23 | ## Setup ## 24 | 25 | Edit the `jamfMigrator.sh` script and modify the following values: 26 | * `newJSS` 27 | * `oldJSS` 28 | * `jamfAPIUser` 29 | * `jamfAPIPassword` 30 | * If you change the LaunchDaemon file and Label names, you'll need to update those in all files as well 31 | * If your JSS has a self signed cert, you may need to add `-k` (`--insecure`) to the `curl` command to disable SSL verification 32 | 33 | API Permissions I needed were: 34 | * JSS Objects > Update > 35 | * Computers 36 | * Users 37 | 38 | Create a payload-free package with all three files and a QuickAdd package created for the new JSS instance 39 | * If using `munkipkg`, add all files into the scripts folder 40 | * If using Packages, add the `postinstall.sh` script as the Post-installation script and all other files into Scripts > Additional Resources 41 | 42 | Grab the `reissue_FileVaultPRK.sh` script [here](https://github.com/MLBZ521/macOS.JAMF/blob/master/Scripts/reissue_FileVaultPRK.sh) 43 | 44 | Upload these items: 45 | * `jamfMigrator.pkg` to the old JSS 46 | * `reissue_FileVaultPRK.sh` to the new JSS 47 | 48 | Create the following Policies: 49 | * In the Old JSS 50 | * *(If needed)* Policy to Create a FileVault Enabled User. [Example](https://github.com/MLBZ521/jamfMigrator/blob/master/Create%20FV_enabled%20User.png) 51 | * Policy to deploy the `jamfMigrator.pkg` Package 52 | * In the New JSS 53 | * Policy with the `reissue_FileVaultPRK.sh` with a known FileVault Unlock Key. [Example](https://github.com/MLBZ521/jamfMigrator/blob/master/FV2%20Reissue%20Script.png) 54 | 55 | 56 | ## Logic ## 57 | 58 | #### jamfMigrator.sh #### 59 | 60 | This script does the heavy lifting. 61 | 62 | * Checks if it can contact the old JSS (uses `jamf checkJSSConnection`) 63 | * If successful, continues 64 | * If an unsuccessful attempt to connect to the old JSS, quit and try again on the next interval 65 | * Sends a generated xml file to the old JSS, via the API, to mark the machine as unmanaged 66 | * The UUID is grabbed from the machine to associate it to its' Jamf Record 67 | * Jamf Framework is removed 68 | * QuickAdd package for the new JSS is installed 69 | * Checks if it can contact the new JSS (uses `jamf checkJSSConnection`) 70 | * If successful, continues 71 | * If unsuccessful 72 | * If still attempting to connect to the old JSS, quit and try again on the next interval 73 | * If it attempts to connect to the new JSS, but fails, quit and try again on the next interval 74 | * Begins 'tearDown' -- cleans up all staged bits 75 | 76 | After virtually every action, the Exit Code (`$?`) is checked to see if it was successful; if it isn't, the script exists and will be launched again at the next interval. 77 | 78 | Logs each process step to `system.log` 79 | 80 | 81 | #### com.github.mlbz521.jamfMigrator.plist #### 82 | 83 | This is the LaunchDaemon that staged and loaded by the `postinstall.sh` script. 84 | 85 | * On load, the LaunchDaemon will execute the `jamfMigrator.sh script` as a child process 86 | * If the script fails, the LaunchDaemon will launch again in ten minutes 87 | 88 | 89 | #### postinstall.sh #### 90 | 91 | This script stages the bits to do the work. You can use it with any payload-free pkg creation method (munkipkg, Packages, etc). 92 | 93 | * On install of the payload-free package, it will stage the bits: 94 | * `cp com.github.mlbz521.jamfMigrator.plist /Library/LaunchDaemons/` 95 | * `cp jamfMigrator.sh /private/var/tmp/` 96 | * `cp QuickAdd.pkg /private/var/tmp/` 97 | 98 | * And then load the LaunchDaemon based on the OS Version 99 | * if >= 10.11 100 | * `/bin/launchctl bootstrap system $launchDaemonLocation` 101 | * `/bin/launchctl enable system/$launchDaemonLabel` 102 | * if <= 10.10 103 | * `/bin/launchctl load $launchDaemonLocation` 104 | 105 | 106 | #### reissue_FileVaultPRK.sh #### 107 | 108 | This script issues a new FileVault Personal Recovery Key. It will require a known FileVault Unlock Key, which can be one of the following: 109 | * A FileVault Enable Account with a known password 110 | * Current FileVault Personal Recovery Key 111 | 112 | Obviously, the easiest and most 'scopable' way to do this to have a FileVault Enabled Account. If you do not have a known FileVault Enable Account with a known password, then on ≤10.12, you can easily create one via Jamf before migrating to the new JSS. (Jamf cannot create a FV_Enabled account on 10.13+ machines currently.) 113 | 114 | Add the script to a Policy in the new JSS and enter the known Unlock Key in the Script Parameter. 115 | 116 | After migrating and after this policy successfully runs (i.e. the new JSS has a **valid** FileVault Recovery Key) the account can be FV disabled or deleted. 117 | -------------------------------------------------------------------------------- /jamfMigrator.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ################################################################################################### 4 | # Script Name: jamfMigrator.sh 5 | # By: Zack Thompson / Created: 11/17/2017 6 | # Version: 1.0 / Updated: 11/21/2017 / By: ZT 7 | # 8 | # Description: This script uses the Jamf API to mark the device as unmanaged, remove the current Jamf Framework, then install a new QuickAdd package, and finally cleanup after itself. 9 | # 10 | # Inspired by several discussions on JamfNation: 11 | # https://www.jamf.com/jamf-nation/discussions/10866/un-manage-and-keep-in-inventory 12 | # https://www.jamf.com/jamf-nation/discussions/10456/remove-framework-after-imaging 13 | # And several other threads I've read regarding Jamf managed state recovery methods (See: rtrouton's CasperCheck) 14 | # 15 | ################################################################################################### 16 | 17 | /usr/bin/logger -s "***** jamfMigrator process: START *****" 18 | 19 | ################################################## 20 | # Define Variables 21 | newJSS="https://newjss.company.com:8443" 22 | oldJSS="https://oldjss.company.com.edu:8443" 23 | jamfAPIUser="APIUsername" 24 | jamfAPIPassword="APIPassword" 25 | jamfURL="${oldJSS}/JSSResource/computers/udid" 26 | jamfBinary="/usr/local/bin/jamf" 27 | getUUID=$(/usr/sbin/ioreg -rd1 -c IOPlatformExpertDevice | /usr/bin/awk '/IOPlatformUUID/ { split($0, line, "\""); printf("%s\n", line[4]); }') 28 | osVersion=$(sw_vers -productVersion | /usr/bin/awk -F '.' '{print $2}') 29 | unmanagePayload="/private/tmp/unmanage_UUID.xml" 30 | launchDaemonLabel="com.github.mlbz521.jamfMigrator" 31 | launchDaemonLocation="/Library/LaunchDaemons/${launchDaemonLabel}.plist" 32 | enrollPkg="/private/var/tmp/QuickAdd.pkg" 33 | 34 | # Stage the "unmanage" PUT payload 35 | /bin/echo " 36 | 37 | 38 | 39 | false 40 | 41 | 42 | " > $unmanagePayload 43 | 44 | ################################################## 45 | # Setup Functions 46 | 47 | function checkJSSConnection { 48 | if [[ -e $jamfBinary ]]; then 49 | checkAvailablity=$(${jamfBinary} checkJSSConnection) 50 | # Function exitStatus 51 | exitStatus $? "${1} is unavailable at this time. Suspending until next interval..." 52 | 53 | if [[ $checkAvailablity == *"${1}"* ]]; then 54 | # If the check contains the JSS we're expecting... 55 | if [[ $checkAvailablity != *"The JSS is available"* ]]; then 56 | # If the JSS is unavailable, suspend further processing... 57 | /usr/bin/logger -s "${1} is unavailable at this time. Suspending until next interval..." 58 | exit 1 59 | else 60 | # If the JSS is available, then continue... 61 | /usr/bin/logger -s "${1} is available, continuing..." 62 | fi 63 | elif [[ $checkAvailablity == *"${oldJSS}"* ]]; then 64 | # If the check is the oldJSS when we're expecting the newJSS, something went wrong, suspend further processing... 65 | /usr/bin/logger -s "Failed -- still pointing to the old JSS Server... Suspending until next interval..." 66 | exit 1 67 | elif [[ $checkAvailablity == *"${newJSS}"* ]]; then 68 | # This elif is for, if somehow on the first checkJSSConnection run, it's connected to the new JSS, then we're good to go. 69 | /usr/bin/logger -s "Connected to the new JSS instance!" 70 | /usr/bin/logger -s "***** jamfMigrator process: COMPLETE *****" 71 | exit 0 72 | fi 73 | else 74 | /usr/bin/logger -s "Unable to run \`jamf checkJSSConnection\`" 75 | /usr/bin/logger -s "Assuming Jamf Framework has been removed..." 76 | # Function enrollMachine 77 | enrollMachine 78 | fi 79 | } 80 | 81 | function enrollMachine { 82 | # Enroll machine 83 | /usr/bin/logger -s "Installing QuickAdd package" 84 | /usr/sbin/installer -dumplog -verbose -pkg $enrollPkg -allowUntrusted -target / 85 | # Function exitStatus 86 | exitStatus $? 87 | } 88 | 89 | function tearDown { 90 | # Unload LaunchDaemon 91 | /usr/bin/logger -s "Unloading LaunchDaemon" 92 | # Determine proper launchctl syntax 93 | if [[ ${osVersion} -ge 11 ]]; then 94 | /bin/launchctl bootout system $launchDaemonLocation 95 | elif [[ ${osVersion} -le 10 ]]; then 96 | /bin/launchctl unload $launchDaemonLocation 97 | fi 98 | # Function exitStatus 99 | exitStatus $? 100 | 101 | # Remove LaunchDaemon 102 | /usr/bin/logger -s "Deleting LaunchDaemon" 103 | /bin/rm -f $launchDaemonLocation 104 | # Function exitStatus 105 | exitStatus $? 106 | 107 | # Delete QuickAdd Package 108 | /usr/bin/logger -s "Deleting QuickAdd Package" 109 | /bin/rm -f $enrollPkg 110 | # Function exitStatus 111 | exitStatus $? 112 | 113 | # Delete Self 114 | /usr/bin/logger -s "Deleting Script" 115 | /bin/rm -f "$0" 116 | # Function exitStatus 117 | exitStatus $? 118 | } 119 | 120 | function exitStatus { 121 | if [[ $1 != "0" ]]; then 122 | /usr/bin/logger -s " -> Failed" 123 | if [[ -e $2 ]]; then 124 | /usr/bin/logger -s "Error: ${2}" 125 | fi 126 | exit 1 127 | else 128 | /usr/bin/logger -s " -> Success!" 129 | fi 130 | } 131 | 132 | ################################################## 133 | # Now that we have our work setup... 134 | 135 | /usr/bin/logger -s "Checking if the current JSS instance is available..." 136 | # Function checkJSSConnection 137 | checkJSSConnection $oldJSS 138 | 139 | # Submit unamange payload to the JSS (add -k, --insecure to disabled SSL verification) 140 | /usr/bin/logger -s "Sending API Payload to Unmanage Computer" 141 | /usr/bin/curl --silent --show-error --fail --user "${jamfAPIUser}:${jamfAPIPassword}" "${jamfURL}/${getUUID}" --header "Content-Type: text/xml" --upload-file $unmanagePayload --request PUT 142 | # Function exitStatus 143 | exitStatus $? 144 | 145 | # Remove JAMF Binary 146 | /usr/bin/logger -s "Removing Framework" 147 | $jamfBinary removeFramework 148 | # Function exitStatus 149 | exitStatus $? 150 | 151 | # Function enrollMachine 152 | enrollMachine 153 | 154 | /usr/bin/logger -s "Checking if new JSS instance is available..." 155 | # Function checkJSSConnection 156 | checkJSSConnection $newJSS 157 | 158 | # Function tearDown 159 | tearDown 160 | 161 | /usr/bin/logger -s "***** jamfMigrator process: COMPLETE *****" 162 | 163 | exit 0 164 | --------------------------------------------------------------------------------