-
Notifications
You must be signed in to change notification settings - Fork 20
/
jssMigrationUtility.bash
executable file
·901 lines (829 loc) · 36.6 KB
/
jssMigrationUtility.bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
#!/bin/bash
# JSS Migration Utility
# Version 1.23
########################################################################
########################################################################
######## Please edit information below to suit your migration ##########
########################################################################
########################################################################
# About JSS URL Variables
# You MUST include the "https://" prefix AND the ":port/" suffix (where applicable)
# For example, if your JSS address is "jss.mycompany.com"
# and it runs on port 8443, the URL should be
# "https://jss.mycompany.com:8443/"
# If the same JSS runs on port 443,
# the URL should be "https://jss.mycompany.com/"
# Edit variables below to suit your environment
sourceJSS="https://jss01.mycompany.com:8443/"
destinationJSS="https://jss02.mycompany.com:8443/"
# Output directory - please choose a directory that you have write access to
# Default is Desktop - "JSS_Migration"
localOutputDirectory="$HOME/Desktop/JSS_Migration"
########################################################################
########################################################################
############# Edit below this section at your own risk ! ###############
########################################################################
########################################################################
# Setting IFS Env to only use new lines as field seperator
IFS=$'\n'
openingDialog ()
{
clear
echo -e "\n--------------------\n--------------------\n\nBeginning JSS Migration Utility\n\n--------------------\n--------------------\n"
echo "
It is critical that you have accounts on both JSS instances with the correct
API access levels. On the source JSS, you need read access. On the destination JSS,
you need full read and write access.
At any time during this script, you may abort with the standard Control - C keys."
echo -e "\n\n--------------------\n--------------------\n\n"
echo "Please enter your JSS API account name for"
read -p "$sourceJSS : " sourceJSSuser
echo -e "\nPlease enter your JSS API password for"
read -p "$sourceJSS : " -s sourceJSSpw
echo -e "\n\nAre your credentials the same for $destinationJSS ?"
read -p "(y or n) : " sameCredentialsChoice
if [ "$sameCredentialsChoice" = y ]
then
destinationJSSuser="$sourceJSSuser"
destinationJSSpw="$sourceJSSpw"
else
echo -e "\n"
echo -e "Please enter your JSS API account name for"
read -p "$destinationJSS : " destinationJSSuser
echo -e "\nPlease enter your JSS API password for"
read -p "$destinationJSS : " -s destinationJSSpw
fi
clear
echo -e "\n--------------------\n--------------------\n"
echo "Checking System..."
echo -e "\nMaking sure we can write files to output directory..."
if [ -d $localOutputDirectory ]
then
echo "Output directory exists. Making sure we can write files inside..."
if [ -d "$localOutputDirectory"/authentication_check ]
then
echo "Found previous authentication check directory. Deleting..."
rm -rf "$localOutputDirectory"/authentication_check
if (( $? == 0 ))
then echo "Success."
else
echo "Failure. There is a problem with permissions in your output directory. Aborting..."
exit 1
fi
fi
else
echo "Creating top level output directory..."
mkdir $localOutputDirectory
if (( $? == 0 ))
then echo "Success."
else
echo "Failure. There is a problem with permissions in your output directory. Aborting..."
exit 1
fi
chmod 775 $localOutputDirectory
fi
echo "Creating authentication check directory..."
mkdir "$localOutputDirectory"/authentication_check
chmod 775 "$localOutputDirectory"/authentication_check
echo -e "\n*****\nEverything looks good with your working directory\n*****\n"
echo "Would you like to run a quick authentication check?"
echo "This will entail creating a mock category in destination JSS"
read -p "( \"y\" or \"n\" ) " authCheckChoice
case $authCheckChoice in
[yY] | [Yy][Ee][Ss])
echo "Proceeding to test your credentials. Downloading categories resource..."
curl -k "$sourceJSS"JSSResource/categories --user "$sourceJSSuser:$sourceJSSpw" -H "Accept: text/xml" > "$localOutputDirectory"/authentication_check/raw.xml
curlStatus=$?
if (( $curlStatus == 0 ))
then
echo -e "\nAble to communicate with $sourceJSS"
else
echo -e "\n\nUnable to communicate with $sourceJSS"
echo "Please check exit status $curlStatus in curl documentation for more details"
echo "You may simply have a typo in your source JSS URL or there may be a network issue"
echo -e "\n!!!!!!!!!!!!!!!!!!!!\nCURL ERROR - TERMINATING\n!!!!!!!!!!!!!!!!!!!!\n\n"
exit 1
fi
#Authentication checks
if [[ $( cat "$localOutputDirectory"/authentication_check/raw.xml | grep "The request requires user authentication" ) ]]
then
echo -e "\nThere is a problem with your credentials for $sourceJSS\n"
echo -e "\n!!!!!!!!!!!!!!!!!!!!\nAUTHENTICATION ERROR - TERMINATING\n!!!!!!!!!!!!!!!!!!!!\n\n"
exit 1
else echo "Credentials check out for $sourceJSS"
fi
echo -e "\nTo check your API write access to $destinationJSS \nwe will attempt to create a test category\n"
echo "It will be named \"zzzz_Migration_Test_\", with a timestamp suffix"
echo "Delete later if you wish"
echo -e "\nAttempting post now...\n"
curl -k "$destinationJSS"JSSResource/categories/id/0 --user "$destinationJSSuser:$destinationJSSpw" -H "Content-Type: text/xml" -X POST -d "<category><name>zzzz_Migration_Test_$( date +%Y%m%d%H%M%S )</name><priority>20</priority></category>" > "$localOutputDirectory"/authentication_check/postCheck.xml
curlStatus=$?
if (( $curlStatus == 0 ))
then
echo -e "\nAble to communicate with $destinationJSS"
else
echo -e "\n\nUnable to communicate with $destinationJSS"
echo "Please check exit status $curlStatus in curl documentation for more details"
echo "You may simply have a typo in your destination JSS URL or there may be a network issue"
echo -e "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\nCURL ERROR - TERMINATING\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n"
exit 1
fi
if [[ $( cat "$localOutputDirectory"/authentication_check/postCheck.xml | grep "The request requires user authentication" ) ]]
then
echo -e "\nThere is a problem with your credentials for $destinationJSS\n"
echo -e "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\nAUTHENTICATION ERROR - TERMINATING\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n"
exit 1
else echo "Credentials check out for $destinationJSS"
fi
echo "Are you ready to proceed?"
read -p "( \"y\" or \"n\" ) : " proceedChoice
case $proceedChoice in
[nN] | [Nn][Oo])
echo "Aborting..."
exit 1
;;
*)
clear
;;
esac
;;
esac
}
initializeDirectoriesPerResource ()
{
echo "Creating local directories for $jssResource ..."
if [ -d "$localOutputDirectory"/"$jssResource" ]
then
echo "Found existing directory for $jssResource -- Archiving..."
if [ -d "$localOutputDirectory"/archives ]
then
echo "Archive directory exists"
else
echo "Archive directory does not exist. Creating..."
mkdir "$localOutputDirectory"/archives
fi
ditto -ck "$localOutputDirectory"/"$jssResource" "$localOutputDirectory"/archives/"$jssResource"-$( date +%Y%m%d%H%M%S ).zip
echo "Removing previous local directory structure for $jssResource"
rm -rf "$localOutputDirectory"/"$jssResource"
else
echo "No previous directories found for $jssResource"
fi
mkdir -p "$localOutputDirectory"/"$jssResource"/id_list
mkdir -p "$localOutputDirectory"/"$jssResource"/fetched_xml
mkdir -p "$localOutputDirectory"/"$jssResource"/parsed_xml
echo -e "\nDirectories created\n"
}
setVariablesForResource ()
{
formattedList="$localOutputDirectory"/"$jssResource"/id_list/formattedList.xml
plainList="$localOutputDirectory"/"$jssResource"/id_list/plainList
plainListAccountsUsers="$localOutputDirectory"/"$jssResource"/id_list/plainListAccountsUsers
plainListAccountsGroups="$localOutputDirectory"/"$jssResource"/id_list/plainListAccountsGroups
resultInt=1
fetchedResult="$localOutputDirectory"/"$jssResource"/fetched_xml/result"$resultInt".xml
fetchedResultAccountsUsers="$localOutputDirectory"/"$jssResource"/fetched_xml/userResult"$resultInt".xml
fetchedResultAccountsGroups="$localOutputDirectory"/"$jssResource"/fetched_xml/groupResult"$resultInt".xml
}
createIDlist ()
{
echo -e "\nFetching XML data for $jssResource ID's"
curl -k "$sourceJSS"JSSResource/$jssResource --user "$sourceJSSuser:$sourceJSSpw" -H "Accept: text/xml" | xmllint --format - > $formattedList
if [ $jssResource = "accounts" ]
then
echo "For accounts resource - we need two separate lists"
echo "Creating plain list of user ID's..."
cat $formattedList | sed '/<site>/,/<\/site>/d' | sed '/<groups>/,/<\/groups>/d' | awk -F '<id>|</id>' '/<id>/ {print $2}' > $plainListAccountsUsers
echo "Creating plain list of group ID's..."
cat $formattedList | sed '/<site>/,/<\/site>/d'| sed '/<users>/,/<\/users>/d' | awk -F '<id>|</id>' '/<id>/ {print $2}' > $plainListAccountsGroups
else
echo -e "\n\nCreating a plain list of $jssResource ID's \n"
cat $formattedList |awk -F'<id>|</id>' '/<id>/ {print $2}' > $plainList
fi
echo -e "\n\n\n"
sleep 3
}
fetchResourceID ()
{
if [ $jssResource = "accounts" ]
then
totalFetchedIDsUsers=$( cat "$plainListAccountsUsers" | wc -l | sed -e 's/^[ \t]*//' )
for userID in $( cat $plainListAccountsUsers )
do
echo "Downloading User ID number $userID ( $resultInt out of $totalFetchedIDsUsers )"
fetchedResultAccountsUsers=$( curl --silent -k --user "$sourceJSSuser:$sourceJSSpw" -H "Accept: text/xml" -X GET "$sourceJSS"JSSResource/accounts/userid/$userID | xmllint --format - )
let "resultInt = $resultInt + 1"
# read the object's JSS ID from the XML
itemID=$( echo "$fetchedResultAccountsUsers" | grep "<id>" | awk -F '<id>|</id>' '{ print $2; exit; }')
# read the object's JSS name from the XML
itemName=$( echo "$fetchedResultAccountsUsers" | grep "<name>" | awk -F '<name>|</name>' '{ print $2; exit; }')
# remove any colons, forward slashes and back slashes from the object's name
cleanedName=$( echo "$itemName" | sed 's/[:\/\\]//g' )
# create a file name using name of the object and its ID
fileName="$cleanedName [ID $itemID]"
# write the XML to the new file name
echo "$fetchedResultAccountsUsers" > "$localOutputDirectory"/"$jssResource"/fetched_xml/user_"$fileName.xml"
done
resultInt=1
totalFetchedIDsGroups=$( cat "$plainListAccountsGroups" | wc -l | sed -e 's/^[ \t]*//' )
for groupID in $( cat $plainListAccountsGroups )
do
echo "Downloading Group ID number $groupID ( $resultInt out of $totalFetchedIDsGroups )"
fetchedResultAccountsGroups=$( curl --silent -k --user "$sourceJSSuser:$sourceJSSpw" -H "Accept: text/xml" -X GET "$sourceJSS"JSSResource/accounts/groupid/$groupID | xmllint --format - )
let "resultInt = $resultInt + 1"
# read the object's JSS ID from the XML
itemID=$( echo "$fetchedResultAccountsGroups" | grep "<id>" | awk -F '<id>|</id>' '{ print $2; exit; }')
# read the object's JSS name from the XML
itemName=$( echo "$fetchedResultAccountsGroups" | grep "<name>" | awk -F '<name>|</name>' '{ print $2; exit; }')
# remove any colons, forward slashes and back slashes from the object's name
cleanedName=$( echo "$itemName" | sed 's/[:\/\\]//g' )
# create a file name using name of the object and its ID
fileName="$cleanedName [ID $itemID]"
# write the XML to the new file name
echo "$fetchedResultAccountsGroups" > "$localOutputDirectory"/"$jssResource"/fetched_xml/group_"$fileName.xml"
done
else
totalFetchedIDs=$( cat "$plainList" | wc -l | sed -e 's/^[ \t]*//' )
for apiID in $( cat $plainList )
do
echo "Downloading ID number $apiID ( $resultInt out of $totalFetchedIDs )"
fetchedResult=$( curl --silent -k --user "$sourceJSSuser:$sourceJSSpw" -H "Accept: text/xml" -X GET "$sourceJSS"JSSResource/$jssResource/id/$apiID | xmllint --format - )
let "resultInt = $resultInt + 1"
# read the object's JSS ID from the XML
itemID=$( echo "$fetchedResult" | grep "<id>" | awk -F '<id>|</id>' '{ print $2; exit; }')
# read the object's JSS name from the XML
itemName=$( echo "$fetchedResult" | grep "<name>" | awk -F '<name>|</name>' '{ print $2; exit; }')
# remove any colons, forward slashes and back slashes from the object's name
cleanedName=$( echo "$itemName" | sed 's/[:\/\\]//g' )
# create a file name using name of the object and its ID
fileName="$cleanedName [ID $itemID]"
# write the XML to the new file name
echo "$fetchedResult" > "$localOutputDirectory"/"$jssResource"/fetched_xml/"$fileName.xml"
done
fi
}
parseResourceID ()
{
echo -e "\n\nProceeding to parse each downloaded XML file..."
if [ $jssResource = "accounts" ]
then
echo -e "\n**********\n\nVery Important Info regarding Accounts -- "
echo -e "\n\n1. If you have LDAP-based JSS Admin accounts, you must migrate LDAP Servers first."
echo -e "2. Passwords WILL NOT be included with standard accounts. Must enter manually in web app \n\n"
read -p "Press RETURN key to acknowledge this message " returnChoice
echo -e "\n\n"
for resourceXML in $( ls "$localOutputDirectory"/"$jssResource"/fetched_xml )
do
echo "Parsing $resourceXML "
cat "$localOutputDirectory"/"$jssResource"/fetched_xml/$resourceXML | grep -v "<id>" > "$localOutputDirectory"/"$jssResource"/parsed_xml/"$resourceXML"
done
elif [ $jssResource = "computergroups" ]
then
echo -e "\n**********\n\nVery Important Info regarding Computer Groups -- "
echo -e "\n\n1. Smart Computer Groups will only contain logic. Will not contain members"
echo "2. Static Computer groups will only contain name and site membership."
echo "3. Unfortunately, you will need to add computers back to Static groups after computers enroll in new jSS"
read -p "Press RETURN key to acknowledge this message " returnChoice
echo -e "\nParsing computer groups to EXCLUDE computers in both static and smart groups..."
for resourceXML in $( ls "$localOutputDirectory"/"$jssResource"/fetched_xml )
do
echo "Parsing $resourceXML "
if [[ $( cat "$localOutputDirectory"/"$jssResource"/fetched_xml/$resourceXML | grep "<is_smart>false</is_smart>" ) ]]
then
echo "$resourceXML is a STATIC computer group..."
cat "$localOutputDirectory"/"$jssResource"/fetched_xml/$resourceXML | grep -v "<id>" | sed '/<computers>/,/<\/computers/d' > "$localOutputDirectory"/"$jssResource"/parsed_xml/static_group_"$resourceXML"
else
echo "$resourceXML is a SMART computer group..."
cat "$localOutputDirectory"/"$jssResource"/fetched_xml/$resourceXML | grep -v "<id>" | sed '/<computers>/,/<\/computers/d' > "$localOutputDirectory"/"$jssResource"/parsed_xml/smart_group_"$resourceXML"
fi
done
elif [ $jssResource = "advancedcomputersearches" ]
then
for resourceXML in $( ls "$localOutputDirectory"/"$jssResource"/fetched_xml )
do
echo "Parsing $resourceXML "
echo "$resourceXML is an Advanced Computer Search..."
cat "$localOutputDirectory"/"$jssResource"/fetched_xml/$resourceXML | grep -v "<id>" | sed '/<computers>/,/<\/computers/d' > "$localOutputDirectory"/"$jssResource"/parsed_xml/"$resourceXML"
done
elif [ $jssResource = "advancedmobiledevicesearches" ]
then
for resourceXML in $( ls "$localOutputDirectory"/"$jssResource"/fetched_xml )
do
echo "Parsing $resourceXML "
echo "$resourceXML is an Advanced Mobile Device Search..."
cat "$localOutputDirectory"/"$jssResource"/fetched_xml/$resourceXML | grep -v "<id>" | sed '/<mobile_devices>/,/<\/mobile_devices/d' > "$localOutputDirectory"/"$jssResource"/parsed_xml/"$resourceXML"
done
elif [ $jssResource = "distributionpoints" ]
then
echo -e "\n**********\n\nVery Important Info regarding Distribution Points -- "
echo -e "\n\n1. Failover settings will NOT be included in migration!"
echo "2. Passwords for Casper Read and Casper Admin accounts will NOT be included in migration!"
echo -e "\nThese must be set manually in web app\n\n**********\n\n"
read -p "Press RETURN key to acknowledge this message " returnChoice
for resourceXML in $( ls "$localOutputDirectory"/"$jssResource"/fetched_xml )
do
echo "Parsing $resourceXML "
cat "$localOutputDirectory"/"$jssResource"/fetched_xml/$resourceXML | grep -v "<id>" | sed '/<failover_point>/,/<\/failover_point_url>/d' > "$localOutputDirectory"/"$jssResource"/parsed_xml/"$resourceXML"
done
elif [ $jssResource = "ldapservers" ]
then
echo -e "\n**********\n\nVery Important Info regarding LDAP Servers -- "
echo -e "\nPasswords for authenticating to LDAP will NOT be included!"
echo -e "You must enter passwords for LDAP in web app\n\n**********\n\n"
read -p "Press RETURN key to acknowledge this message " returnChoice
for resourceXML in $( ls "$localOutputDirectory"/"$jssResource"/fetched_xml )
do
echo "Parsing $resourceXML "
cat "$localOutputDirectory"/"$jssResource"/fetched_xml/$resourceXML | grep -v "<id>" > "$localOutputDirectory"/"$jssResource"/parsed_xml/"$resourceXML"
done
elif [ $jssResource = "directorybindings" ]
then
echo -e "\n**********\n\nVery Important Info regarding Directory Bindings -- "
echo -e "\nPasswords for directory binding account will NOT be included!"
echo -e "You must set these passwords for LDAP in web app\n\n**********\n\n"
read -p "Press RETURN key to acknowledge this message " returnChoice
for resourceXML in $( ls "$localOutputDirectory"/"$jssResource"/fetched_xml )
do
echo "Parsing $resourceXML "
cat "$localOutputDirectory"/"$jssResource"/fetched_xml/$resourceXML | grep -v "<id>" > "$localOutputDirectory"/"$jssResource"/parsed_xml/"$resourceXML"
done
elif [ $jssResource = "packages" ]
then
echo -e "\n**********\n\nAbout Packages -- "
echo -e "\nFor packages with no category assigned, we need to strip"
echo -e "the category string from the xml, or it will fail to upload. \n\n**********\n\n"
read -p "Press RETURN key to acknowledge this message " returnChoice
for resourceXML in $( ls "$localOutputDirectory"/"$jssResource"/fetched_xml )
do
echo "Parsing $resourceXML "
if [[ $( cat "$localOutputDirectory"/"$jssResource"/fetched_xml/$resourceXML | grep "No category assigned" ) ]]
then
echo "Stripping category string from $resourceXML"
cat "$localOutputDirectory"/"$jssResource"/fetched_xml/$resourceXML | grep -v "<id>" | sed '/<category>/,/<\/category/d' > "$localOutputDirectory"/"$jssResource"/parsed_xml/"$resourceXML"
else
cat "$localOutputDirectory"/"$jssResource"/fetched_xml/$resourceXML | grep -v "<id>" > "$localOutputDirectory"/"$jssResource"/parsed_xml/"$resourceXML"
fi
done
elif [ $jssResource = "osxconfigurationprofiles" ]
then
echo -e "\n**********\n\nImportant note regarding OS X Configuration Profiles -- "
echo -e "\nIt is critical that computer groups are migrated first!"
echo "Data regarding which computers have profiles will be stripped."
echo -e "This data will come back as computers enroll in destination JSS\n\n**********\n\n"
read -p "Press RETURN key to acknowledge this message " returnChoice
for resourceXML in $( ls "$localOutputDirectory"/"$jssResource"/fetched_xml )
do
echo "Parsing $resourceXML "
cat "$localOutputDirectory"/"$jssResource"/fetched_xml/$resourceXML | grep -v "<id>" | sed '/<computers>/,/<\/computers/d' > "$localOutputDirectory"/"$jssResource"/parsed_xml/"$resourceXML"
done
elif [ $jssResource = "restrictedsoftware" ]
then
echo -e "\n**********\n\nImportant note regarding Restricted Software -- "
echo -e "\nIt is critical that the following items are migrated first!"
echo "1. Computer Groups"
echo "2. Buildings"
echo "3. Departments"
echo -e "\nIndividual computers that are excluded from restricted software items \nWILL NOT be included in migration!"
echo "They will need to be added later after they re-enroll"
read -p "Press RETURN key to acknowledge this message " returnChoice
echo -e "\nParsing Restricted Software ID's to EXCLUDE computers..."
for resourceXML in $( ls "$localOutputDirectory"/"$jssResource"/fetched_xml )
do
echo "Parsing $resourceXML "
cat "$localOutputDirectory"/"$jssResource"/fetched_xml/$resourceXML | grep -v "<id>" | sed '/<computers>/,/<\/computers/d' > "$localOutputDirectory"/"$jssResource"/parsed_xml/"$resourceXML"
done
elif [ $jssResource = "policies" ]
then
echo -e "\n**********\n\nVery Important Info regarding Policies -- "
echo -e "\n1. Policies that are not assigned to a category will NOT be migrated"
echo " Reason: want to avoid migrating one-off policies generated by Casper Remote "
echo "2. The following items will not be migrated "
echo " a. Individual computers as a scope, exclusion, etc."
echo " b. Self Service icons"
echo -e "\nThese items must be added manually via web app\n\n**********\n\n"
read -p "Press RETURN key to acknowledge this message " returnChoice
echo -e "\n"
echo "In some environments, posting policies with other limitations and exclusions"
echo "(.e.g. LDAP Users and Groups) causes errors when posting."
echo -e "\nWould you like to omit user and group limitations from your policies and add manually later?"
read -p "( \"y\" or \"n\" ) " omitLimitationsChoice
case $omitLimitationsChoice in
[yY] | [Yy][Ee][Ss])
for resourceXML in $( ls "$localOutputDirectory"/"$jssResource"/fetched_xml )
do
echo "Parsing $resourceXML "
if [[ $( cat "$localOutputDirectory"/"$jssResource"/fetched_xml/$resourceXML | grep "<name>No category assigned</name>" ) ]]
then
echo "Policy $resourceXML is not assigned to a category. Ignoring..."
else
cat "$localOutputDirectory"/"$jssResource"/fetched_xml/$resourceXML | grep -v "<id>" | sed '/<computers>/,/<\/computers>/d' | sed '/<self_service_icon>/,/<\/self_service_icon>/d' | sed '/<limit_to_users>/,/<\/limit_to_users>/d' | sed '/<users>/,/<\/users>/d' | sed '/<user_groups>/,/<\/user_groups>/d' > "$localOutputDirectory"/"$jssResource"/parsed_xml/"$resourceXML"
fi
done
;;
*)
for resourceXML in $( ls "$localOutputDirectory"/"$jssResource"/fetched_xml )
do
echo "Parsing $resourceXML "
if [[ $( cat "$localOutputDirectory"/"$jssResource"/fetched_xml/$resourceXML | grep "<name>No category assigned</name>" ) ]]
then
echo "Policy $resourceXML is not assigned to a category. Ignoring..."
else
cat "$localOutputDirectory"/"$jssResource"/fetched_xml/$resourceXML | grep -v "<id>" | sed '/<computers>/,/<\/computers>/d' | sed '/<self_service_icon>/,/<\/self_service_icon>/d' > "$localOutputDirectory"/"$jssResource"/parsed_xml/"$resourceXML"
fi
done
;;
esac
else
echo "For $jssResource - no need for extra special parsing. Simply removing references to ID's"
sleep 2
for resourceXML in $( ls "$localOutputDirectory"/"$jssResource"/fetched_xml )
do
echo "Parsing $resourceXML "
cat "$localOutputDirectory"/"$jssResource"/fetched_xml/$resourceXML | grep -v "<id>" > "$localOutputDirectory"/"$jssResource"/parsed_xml/"$resourceXML"
done
fi
}
pauseForManualCheck ()
{
echo -e "\n----------------------------------------\nYou may wish to spot-check parsed XML \nin "$localOutputDirectory"/"$jssResource"/parsed_xml \n----------------------------------------"
echo -e "If you enter \"n\" - you will return to the main menu\nIt is safe to just leave the utility in this state while you spot-check parsed XML.\n"
read -p "Continue now ? (y or n) : " continueResponse
case $continueResponse in
[nN] | [Nn][Oo])
jssResource="null"
;;
*)
echo -e "Continuing...\n\n"
sleep 1
;;
esac
}
postResource ()
{
if [ $jssResource = "null" ]
then
# We are not doing anything here.
# This is just a placeholder in case someone wants to return to main menu
# during pauseForManualCheck function
# instead of proceeding with post
echo "Nothing to see here" > /dev/null
else
echo -e "\n\nTime to post $jssResource to destination JSS...\n\n"
sleep 1
fi
if [ $jssResource = "accounts" ]
then
echo "For accounts, we need to post users first, then groups..."
echo -e "\n\n----------\nPosting users...\n"
sleep 1
totalParsedResourceXML_user=$( ls "$localOutputDirectory"/"$jssResource"/parsed_xml/user* | wc -l | sed -e 's/^[ \t]*//' )
postInt_user=0
for xmlPost_user in $( ls "$localOutputDirectory"/"$jssResource"/parsed_xml/user* )
do
let "postInt_user = $postInt_user + 1"
echo -e "\n----------\n----------"
echo -e "\nPosting $parsedXML_user ( $postInt_user out of $totalParsedResourceXML_user ) \n"
curl -k -g "$destinationJSS"JSSResource/accounts/userid/0 --user "$destinationJSSuser:$destinationJSSpw" -H "Content-Type: text/xml" -X POST -T "$xmlPost_user"
done
echo -e "\n\n----------\nPosting groups...\n"
sleep 1
totalParsedResourceXML_group=$( ls "$localOutputDirectory"/"$jssResource"/parsed_xml/group* | wc -l | sed -e 's/^[ \t]*//' )
postInt_group=0
for xmlPost_group in $( ls "$localOutputDirectory"/"$jssResource"/parsed_xml/group* )
do
let "postInt_group = $postInt_group + 1"
echo -e "\n----------\n----------"
echo -e "\nPosting $parsedXML_group ( $postInt_group out of $totalParsedResourceXML_group ) \n"
curl -k -g "$destinationJSS"JSSResource/accounts/groupid/0 --user "$destinationJSSuser:$destinationJSSpw" -H "Content-Type: text/xml" -X POST -T "$xmlPost_group"
done
elif [ $jssResource = "computergroups" ]
then
echo "For computers, we need to post static groups before smart groups,"
echo "because smart groups can contain static groups"
echo -e "\n\n----------\nPosting static computer groups...\n"
totalParsedResourceXML_staticGroups=$( ls "$localOutputDirectory"/computergroups/parsed_xml/static_group_* | wc -l | sed -e 's/^[ \t]*//' )
postInt_static=0
for parsedXML_static in $( ls "$localOutputDirectory"/computergroups/parsed_xml/static_group_* )
do
xmlPost_static=$parsedXML_static
let "postInt_static = $postInt_static + 1"
echo -e "\n----------\n----------"
echo -e "\nPosting $parsedXML_static ( $postInt_static out of $totalParsedResourceXML_staticGroups ) \n"
curl -k -g "$destinationJSS"JSSResource/computergroups/id/0 --user "$destinationJSSuser:$destinationJSSpw" -H "Content-Type: text/xml" -X POST -T "$xmlPost_static"
done
echo -e "\n\n----------\nPosting smart computer groups...\n"
sleep 1
totalParsedResourceXML_smartGroups=$( ls "$localOutputDirectory"/computergroups/parsed_xml/smart_group_* | wc -l | sed -e 's/^[ \t]*//' )
postInt_smart=0
for parsedXML_smart in $( ls "$localOutputDirectory"/computergroups/parsed_xml/smart_group_* )
do
xmlPost_smart=$parsedXML_smart
let "postInt_smart = $postInt_smart + 1"
echo -e "\n----------\n----------"
echo -e "\nPosting $parsedXML_smart ( $postInt_smart out of $totalParsedResourceXML_smartGroups ) \n"
curl -k -g "$destinationJSS"JSSResource/computergroups/id/0 --user "$destinationJSSuser:$destinationJSSpw" -H "Content-Type: text/xml" -X POST -T "$xmlPost_smart"
done
elif [ $jssResource = "advancedcomputersearches" ]
then
totalParsedResourceXML_advancedComputerSearches=$( ls "$localOutputDirectory"/advancedcomputersearches/parsed_xml/* | wc -l | sed -e 's/^[ \t]*//' )
postInt_smart=0
for parsedXML_smart in $( ls "$localOutputDirectory"/advancedcomputersearches/parsed_xml/* )
do
xmlPost_smart=$parsedXML_smart
let "postInt_smart = $postInt_smart + 1"
echo -e "\n----------\n----------"
echo -e "\nPosting $parsedXML_smart ( $postInt_smart out of $totalParsedResourceXML_advancedComputerSearches ) \n"
curl -k -g "$destinationJSS"JSSResource/advancedcomputersearches/id/0 --user "$destinationJSSuser:$destinationJSSpw" -H "Content-Type: text/xml" -X POST -T "$xmlPost_smart"
done
elif [ $jssResource = "advancedmobiledevicesearches" ]
then
totalParsedResourceXML_advancedMobileDeviceSearches=$( ls "$localOutputDirectory"/advancedmobiledevicesearches/parsed_xml/* | wc -l | sed -e 's/^[ \t]*//' )
postInt_smart=0
for parsedXML_smart in $( ls "$localOutputDirectory"/advancedmobiledevicesearches/parsed_xml/* )
do
xmlPost_smart=$parsedXML_smart
let "postInt_smart = $postInt_smart + 1"
echo -e "\n----------\n----------"
echo -e "\nPosting $parsedXML_smart ( $postInt_smart out of $totalParsedResourceXML_advancedMobileDeviceSearches ) \n"
curl -k -g "$destinationJSS"JSSResource/advancedmobiledevicesearches/id/0 --user "$destinationJSSuser:$destinationJSSpw" -H "Content-Type: text/xml" -X POST -T "$xmlPost_smart"
done
else
totalParsedResourceXML=$( ls "$localOutputDirectory"/"$jssResource"/parsed_xml | wc -l | sed -e 's/^[ \t]*//' )
postInt=0
for parsedXML in $( ls "$localOutputDirectory"/"$jssResource"/parsed_xml )
do
xmlPost="$localOutputDirectory"/"$jssResource"/parsed_xml/$parsedXML
let "postInt = $postInt + 1"
echo -e "\n----------\n----------"
echo -e "\nPosting $parsedXML ( $postInt out of $totalParsedResourceXML ) \n"
curl -k -g "$destinationJSS"JSSResource/$jssResource/id/0 --user "$destinationJSSuser:$destinationJSSpw" -H "Content-Type: text/xml" -X POST -T "$xmlPost"
done
fi
if [ $jssResource != "null" ]
then
echo -e "\n\n**********\nPosting complete for $jssResource \n**********\n\n"
read -p "Press RETURN to continue." returnKey
fi
}
manualUpload ()
{
clear
echo -e "\n\nYou have chosen to specify XML files to upload to a given resource."
echo -e "WARNING: No error control for this function."
echo -e "WARNING: Please only continue with this function if you know exactly what you are doing.\n\n"
read -p "API Resource (by name) : " jssResourceManualInput
read -p "Source directory containing XML files : " resultOutputDirectory
echo -e "\nAre you creating new records (POST) or updating existing records (PUT)?"
read -p "Enter \"1\" for POST or \"2\" for PUT : " actionChoice
validChoice=999
until (( $validChoice == 1 ))
do
if (( $actionChoice == 1 ))
then
echo "Proceeding to POST xml files..."
curlAction="POST"
validChoice=1
elif (( $actionChoice == 2 ))
then
echo "Proceeding to PUT xml files..."
curlAction="PUT"
validChoice=1
else
echo "Please enter a valid selection"
read -p "Enter 1 for POST or 2 for PUT : " actionChoice
fi
done
echo -e "\n\n"
totalParsedResourceXML=$( ls "$resultOutputDirectory"/$manualPost | wc -l | sed -e 's/^[ \t]*//' )
postInt=0
for manualPost in $( ls "$resultOutputDirectory" )
do
xmlPost="$resultOutputDirectory"/$manualPost
let "postInt = $postInt + 1"
echo -e "\n----------\n----------"
echo -e "\nPosting $manualPost( $postInt out of $totalParsedResourceXML ) \n"
curl -k -g "$destinationJSS"JSSResource/$jssResourceManualInput --user "$destinationJSSuser:$destinationJSSpw" -H "Content-Type: text/xml" -X "$curlAction" -T "$xmlPost"
done
echo -e "\nExit or return to main menu?"
read -p "( \"x\" or \"m\" ) " exitChoice
case $exitChoice in
[xX] | [Ee][Xx][Ii][Tt])
echo -e "\nExiting..."
exit 0
;;
esac
}
exitDialog ()
{
echo -e "\n\n----------------------------------------\n\nThank you for using the JSS Migration Utility. Goodbye.\n\n----------------------------------------\n"
exit 0
}
displayHelp ()
{
clear
echo "
README
The JSS Migration Utility uses the JAMF API to download resources from a source JSS and
upload those resources to a destination JSS. The utiltiy does NOT migrate computers.
The primary goal and use-case for this utiltiy is to provide a mechanism where a JAMF
admin can set up a barebones, clean JSS and import management resources (categories,
scripts, extension attributes, computer groups, etc.) from another JSS instance. This is
perhaps most helpful when circumstances (e.g. a crufty database) prevent JSS migration via
the usual process - a database restore.
Basic Process:
1. XML files are downloaded from source JSS to local system
2. XML files are parsed, depending on the current resource
3. XML files are then uploaded to destination JSS
WARNINGS:
For this to work correctly, some data must be stripped from the downloaded XML before
uploading to new server. This occurs during the parsing process. Before each group of
resource files are parsed, a warning will display explaining what data, if any, will be
stripped. For example, before the ldapservers resource files are parsed, you will see
this message --
Passwords for authenticating to LDAP will NOT be included!
You must enter passwords for LDAP in web app
Local File System:
By default, XML files are stored to ~/Desktop/JSS_Migration Before work begins on each
resource, the utility looks for the presence of any previously downloaded resources and
archives to ~/Desktop/JSS_Migration/archives if necessary
"
read -p "Press RETURN key to return to main menu " returnChoice
}
displayMainMenu ()
{
clear
echo "
************ MAIN MENU ************
Which JSS resource would you like to migrate?
(WARNING - We strongly encourage you to proceed in order)
1 = Sites
2 = Categories
3 = LDAP Servers
4 = Accounts (JSS Admin Accounts and Groups)
5 = Buildings
6 = Departments
7 = Extension Attributes (for computers)
8 = Directory Bindings
9 = Dock Items
10 = Removable MAC Addresses
11 = Printers
12 = Licensed Software
13 = Scripts
14 = Netboot Servers
15 = Distribution Points
16 = SUS Servers
17 = Network Segments
18 = Computer Groups
19 = OS X Configuration Profiles
20 = Restricted Software
21 = Packages
22 = Policies
23 = Advanced Computer Searches
24 = Advanced Mobile Device Searches
99 = Upload XML files from a specified directory to a specified resource
(Useful if you have hand-edited XML files you need to upload)
? = README
0 = EXIT
"
}
getMainMenuSelection ()
{
validChoice=9999
until (( $validChoice == 1 ))
do
displayMainMenu
read -p "Enter the number which corresponds to the correct resource : " resourceNumber
if [ "$resourceNumber" = "?" ]
then validChoice=1
elif (( $resourceNumber == 1 ))
then
validChoice=1
jssResource="sites"
elif (( $resourceNumber == 2 ))
then
validChoice=1
jssResource="categories"
elif (( $resourceNumber == 3 ))
then
validChoice=1
jssResource="ldapservers"
elif (( $resourceNumber == 4 ))
then
validChoice=1
jssResource="accounts"
elif (( $resourceNumber == 5 ))
then
validChoice=1
jssResource="buildings"
elif (( $resourceNumber == 6 ))
then
validChoice=1
jssResource="departments"
elif (( $resourceNumber == 7 ))
then
validChoice=1
jssResource="computerextensionattributes"
elif (( $resourceNumber == 8 ))
then
validChoice=1
jssResource="directorybindings"
elif (( $resourceNumber == 9 ))
then
validChoice=1
jssResource="dockitems"
elif (( $resourceNumber == 10 ))
then
validChoice=1
jssResource="removablemacaddresses"
elif (( $resourceNumber == 11 ))
then
validChoice=1
jssResource="printers"
elif (( $resourceNumber == 12 ))
then
validChoice=1
jssResource="licensedsoftware"
elif (( $resourceNumber == 13 ))
then
validChoice=1
jssResource="scripts"
elif (( $resourceNumber == 14 ))
then
validChoice=1
jssResource="netbootservers"
elif (( $resourceNumber == 15 ))
then
validChoice=1
jssResource="distributionpoints"
elif (( $resourceNumber == 16 ))
then
validChoice=1
jssResource="softwareupdateservers"
elif (( $resourceNumber == 17 ))
then
validChoice=1
jssResource="networksegments"
elif (( $resourceNumber == 18 ))
then
validChoice=1
jssResource="computergroups"
elif (( $resourceNumber == 19 ))
then
validChoice=1
jssResource="osxconfigurationprofiles"
elif (( $resourceNumber == 20 ))
then
validChoice=1
jssResource="restrictedsoftware"
elif (( $resourceNumber == 21 ))
then
validChoice=1
jssResource="packages"
elif (( $resourceNumber == 22 ))
then
validChoice=1
jssResource="policies"
elif (( $resourceNumber == 23 ))
then
validChoice=1
jssResource="advancedcomputersearches"
elif (( $resourceNumber == 24 ))
then
validChoice=1
jssResource="advancedmobiledevicesearches"
elif (( $resourceNumber == 99 ))
then
validChoice=1
elif (( $resourceNumber == 0 ))
then
validChoice=1
else
echo -e "\n!!!!!!!!!!!!!!!!!!!!\nPlease select a valid number\n!!!!!!!!!!!!!!!!!!!!\n"
sleep 3
fi
done
}
# MAIN
openingDialog
quitLoop=9999
until (( $quitLoop == 1 ))
do
getMainMenuSelection
if [ "$resourceNumber" = "?" ]
then displayHelp
elif [ $resourceNumber = "99" ]
then manualUpload
elif [ $resourceNumber = "0" ]
then
quitLoop=1
exitDialog
else
initializeDirectoriesPerResource
setVariablesForResource
createIDlist
fetchResourceID
parseResourceID
pauseForManualCheck
postResource
fi
done
exit 0