Archive
Scripting Remote Desktop Bookmarks
A few years ago I was searching for a way to easily create bookmarks in Microsoft Remote Desktop 8 on the Mac. Prior to version 8 you could drop an .RDP file on a machine and that was really all you needed to do to give your users the ability to connect to servers. Granted, you can still use this method, it’s just a bit sloppier, in my opinion.
So I went searching for a way to script the bookmarks, and that led me to my good friend Ben Toms’ (@macmuleblog) blog. I found his post, “HOW TO: CREATE A MICROSOFT REMOTE DESKTOP 8 CONNECTION” and started experimenting. After some trial and error, I discovered that using PlistBuddy to create the bookmarks just wasn’t being consistent. So I looked into using the defaults command instead. I finally was able to settle on the following script:
#!/bin/sh | |
# date: 18 Jun 2014 | |
# Name: RDC-Connection.sh | |
# Author: Steve Wood (swood@integer.com) | |
# updated: 29 Feb 2016 - included line to add remote program to start on connection for @gmarnin | |
# grab the logged in user's name | |
loggedInUser=`/bin/ls -l /dev/console | /usr/bin/awk '{ print $3 }'` | |
# global | |
RDCPLIST=/Users/$loggedInUser/Library/Containers/com.microsoft.rdc.mac/Data/Library/Preferences/com.microsoft.rdc.mac.plist | |
myUUID=`uuidgen` | |
LOGPATH='/private/var/inte/logs' | |
# set variables | |
connectionName="NAME YOUR CONNECTION" | |
hostAddress="SERVERIPADDRESS" | |
# if you need to put an AD domain name, put it in the userName variable, otherwise leave blank | |
userName='DOMAINNAME\' | |
userName+=$loggedInUser | |
resolution="1280 1024" | |
colorDepth="32" | |
fullScreen="FALSE" | |
scaleWindow="FALSE" | |
useAllMonitors="TRUE" | |
set -xv; exec 1> $LOGPATH/rdcPlist.txt 2>&1 | |
defaults write $RDCPLIST bookmarkorder.ids -array-add "'{$myUUID}'" | |
defaults write $RDCPLIST bookmarks.bookmark.{$myUUID}.label -string "$connectionName" | |
defaults write $RDCPLIST bookmarks.bookmark.{$myUUID}.hostname -string $hostAddress | |
defaults write $RDCPLIST bookmarks.bookmark.{$myUUID}.username -string $userName | |
defaults write $RDCPLIST bookmarks.bookmark.{$myUUID}.resolution -string "@Size($resolution)" | |
defaults write $RDCPLIST bookmarks.bookmark.{$myUUID}.depth -integer $colorDepth | |
defaults write $RDCPLIST bookmarks.bookmark.{$myUUID}.fullscreen -bool $fullScreen | |
defaults write $RDCPLIST bookmarks.bookmark.{$myUUID}.scaling -bool $scaleWindow | |
defaults write $RDCPLIST bookmarks.bookmark.{$myUUID}.useallmonitors -bool $useAllMonitors | |
#comment out the following if you do not need to execute a program on start of connection | |
# You can adjust the string to be any app that is installed. | |
defaults write $RDCPLIST bookmarks.bookmark.{$myUUID}.remoteProgram -string "C:\\\\Program Files\\\\\\\\Windows NT\\\\Accessories\\\\wordpad.exe" | |
chown -R "$loggedInUser:staff" /Users/$loggedInUser/Library/Containers/com.microsoft.rdc.mac |
You can find that code in my GitHub repo here.
RDC URI Attribute Support
I had posted that script up on JAMF Nation back in June 2014 when someone had asked about deploying connections. Recently user @gmarnin posted to that thread asking if anyone knew how to add an alternate shell key to the script. After no response, he reached out to me on the Twitter (I’m @stevewood_tx in case you care). So, I dusted off my script, fired up my Mac VM, and started experimenting.
The RDC GUI does not allow for a place to add these URI Attributes. I read through that web page and Marnin forwarded me this one as well. Marnin explained that he was able to get it to work when he exported the bookmark as an .RDP file and then used a text editor to add the necessary “alternate shell:s:” information. Armed with this knowledge, I went to the VM and started testing.
First I created a bookmark in a fresh installation of RDC. I had no bookmarks at all. After creating a bookmark I jumped into Terminal and did a read of the plist file and came up with this:
YosemiteVM:Preferences integer$ defaults read /Users/integer/Library/Containers/com.microsoft.rdc.mac/Data/Library/Preferences/com.microsoft.rdc.mac.plist | |
{ | |
QmoteUUIDKey = "ff870b10-7e8e-47c2-98bd-f14f3f0cd1b0"; | |
"bld_number" = 26665; | |
"bookmarklist.expansionStates" = { | |
GENEREAL = 1; | |
}; | |
"bookmarkorder.ids" = ( | |
"{2a3925d6-659e-456e-ab03-86919b30b54b}" | |
); | |
"bookmarks.bookmark.{2a3925d6-659e-456e-ab03-86919b30b54b}.fullscreenMode" = "@Variant(\177\017FullscreenMode\001)"; | |
"bookmarks.bookmark.{2a3925d6-659e-456e-ab03-86919b30b54b}.hostname" = "termserv.company.com"; | |
"bookmarks.bookmark.{2a3925d6-659e-456e-ab03-86919b30b54b}.label" = Test; | |
"bookmarks.bookmark.{2a3925d6-659e-456e-ab03-86919b30b54b}.username" = ""; | |
"connectWindow.geometry" = <01d9d0cb 00010000 000001b4 000000a0 000003b9 0000033f 000001b4 000000fc 000003b9 0000033f 00000000 0000>; | |
"connectWindow.windowState" = <000000ff 00000000 fd000000 00000002 06000002 44000000 04000000 04000000 08000000 08fc0000 00010000 00020000 00010000 000e0074 006f006f 006c0042 00610072 01000000 00ffffff ff000000 00000000 00>; | |
lastdevinfoupd = 1456781093; | |
lastdevresourceupd = 1456781153; | |
"preferences.ignoredhosts" = ( | |
"10.93.209.210:3389" | |
); | |
"preferences.resolutions" = ( | |
"@Size(640 480)", | |
"@Size(800 600)", | |
"@Size(1024 768)", | |
"@Size(1280 720)", | |
"@Size(1280 1024)", | |
"@Size(1600 900)", | |
"@Size(1920 1080)", | |
"@Size(1920 1200)" | |
); | |
"show_whats_new_dialog" = 0; | |
"stored_version_number" = "8.0.26665"; | |
tlmtryOn = 1; | |
} |
Now that we had a baseline, I exported the bookmark to the desktop of the VM, edited it to add the “alternate shell” bits, and then re-imported it into RDC as a new bookmark. I then tested to make sure it would work as advertised. After some trial and error, I was able to get the exact syntax for the “alternate shell” entry to work. Now I just needed to see what changes were made in the plist file. A quick read showed me the following:
YosemiteVM:Preferences integer$ defaults read /Users/integer/Library/Containers/com.microsoft.rdc.mac/Data/Library/Preferences/com.microsoft.rdc.mac.plist | |
{ | |
QmoteUUIDKey = "ff870b10-7e8e-47c2-98bd-f14f3f0cd1b0"; | |
"bld_number" = 26665; | |
"bookmarklist.expansionStates" = { | |
GENEREAL = 1; | |
}; | |
"bookmarkorder.ids" = ( | |
"{2a3925d6-659e-456e-ab03-86919b30b54b}" | |
); | |
"bookmarks.bookmark.{2a3925d6-659e-456e-ab03-86919b30b54b}.fullscreenMode" = "@Variant(\177\017FullscreenMode\001)"; | |
"bookmarks.bookmark.{2a3925d6-659e-456e-ab03-86919b30b54b}.hostname" = "termserv.company.com"; | |
"bookmarks.bookmark.{2a3925d6-659e-456e-ab03-86919b30b54b}.label" = Test; | |
"bookmarks.bookmark.{2a3925d6-659e-456e-ab03-86919b30b54b}.username" = ""; | |
"bookmarks.bookmark.{2a3925d6-659e-456e-ab03-86919b30b54b}.remoteProgram" = "C:\\\\Program Files\\\\\\\\Windows NT\\\\Accessories\\\\wordpad.exe"; | |
"connectWindow.geometry" = <01d9d0cb 00010000 000001b4 000000a0 000003b9 0000033f 000001b4 000000fc 000003b9 0000033f 00000000 0000>; | |
"connectWindow.windowState" = <000000ff 00000000 fd000000 00000002 06000002 44000000 04000000 04000000 08000000 08fc0000 00010000 00020000 00010000 000e0074 006f006f 006c0042 00610072 01000000 00ffffff ff000000 00000000 00>; | |
lastdevinfoupd = 1456781093; | |
lastdevresourceupd = 1456781153; | |
"preferences.ignoredhosts" = ( | |
"10.93.209.210:3389" | |
); | |
"preferences.resolutions" = ( | |
"@Size(640 480)", | |
"@Size(800 600)", | |
"@Size(1024 768)", | |
"@Size(1280 720)", | |
"@Size(1280 1024)", | |
"@Size(1600 900)", | |
"@Size(1920 1080)", | |
"@Size(1920 1200)" | |
); | |
"show_whats_new_dialog" = 0; | |
"stored_version_number" = "8.0.26665"; | |
tlmtryOn = 1; | |
} |
The key is the line that has “remoteProgram” as part of the entry. You have to get the full path on the Windows machine to the application you want to run on connection to the server. Once you know that path, you can adjust your bookmark script however you need.
The script I posted above, and is linked in my GitHub repo, contains the line to add that Remote Program (alternate shell). If you do not need it, just comment it out of the script.
Custom CrashPlan Install With Casper
I’m a fanboy. There, I said it and I’m proud of it. I’m a fanboy of JAMF Software’s Casper Suite. I’m also a fanboy of Code42 and their CrashPlan software. Put them together and it’s like when the two teens discovered peanut butter and chocolate as an amazing combination.
I am all about trying to minimize the amount of time my users need to be interrupted due to IT needs. That’s a large part of the reason we use Casper, so that my users do not have to be inconvenienced. Let’s face it, the more time I take performing IT tasks on their computer that cause them to not be able to work, the less money they are making for our agency. It’s one of my primary tenets of customer support: make every reasonable effort to not disturb the end user, period. So when I discovered several of my end user machines were not backing up via CrashPlan, I needed to find a way to deploy CrashPlan with as little interruption as possible. In steps Casper and CrashPlan together.
Our original setup of CrashPlan that has been running for several years, was setup using local logins. At the time when we first deployed, we were not on a single LDAP implementation, so I didn’t want to deploy an LDAP integrated CrashPlan. Fast forward to now, and we have a single LDAP (AD) and I want to take advantage of that implementation to provide “same password” logins for my users.
Fortunately JAMF has a technical paper outlining how to do this, titled Administering CrashPlan PROe with The Casper Suite. This paper was written back when CrashPlan PROe was still a thing. With the release of version 5 of CrashPlan, it has now become simply Code42 CrashPlan. This document still works for the newer version of the software.
Get The Template
The first step is to get ahold of CrashPlan custom template for the installer. Following the paper, you can download the custom template by navigating to this URL:
http://YourServerAddress:4280/download/CrashPlanPROe_Custom.zip
NOTE: If you are deploying version 5 or higher of CrashPlan, you can use this URL to download a newer version of the kit:
http://YourServerAddress:4280/download/Code42CrashPlan_Custom.zip
While there are two different URLs, you can use either one to customize your install.
Edit Away
After downloading and expanding the zip file, you will need to edit the userInfo.sh
file to set some settings. First of which is to hide the application from your users during installation. Simply set the following line:
startDesktop=false |
The next thing you will want to edit are the user variables. CrashPlan uses these variables to grab the user’s short name and their home folder location. An assumption is made when it comes to the user’s home folder, and that is the assumption that the home folder lives in /Users. If your home folders do not live there, or you want to script the generation using dscl, you can. I’m lazy and so I simply went with the /Users setting.
Also, the method to grab the user short name is based on the user that is logged in currently. Now, we didn’t discuss before how you were deploying this via Casper (login, logout, Self Service, etc), but suffice it to say, it is preferable to deploy this when a user is logged in to the computer. There have been many discussions on JAMF Nation about CrashPlan and how to grab the user, I used the information found in this post to grab the info I needed:
user=`/usr/bin/defaults read /Library/Preferences/com.apple.loginwindow lastUserName` | |
CP_USER_HOME="/Users/$user" | |
userGroup=`id -gn "$user"` | |
CP_USER_NAME="$user" |
Now that you have the edits done, keep going through the technical paper, running the custom.sh
script next to build the Custom folder we will need in a minute, and to download the installers. The custom.sh
script will download the installers for Windows, Mac, and Linux, and slipstream the Custom folder into the installer package for us. In our case, since we are only concerned with the Mac installer, it places a hidden .Custom folder at the root of the DMG. We want that folder. So follow along in the tech paper to mount the Mac installer DMG and copy the .Custom folder out somewhere.
Package It All Up
We are going to need to deploy these custom settings alongside the CrashPlan installer. The tech paper has you using Composer (no surprise since it is their product), but I personally like to use Packages for my packaging fun. I’m not going to get into a discussion about what the best packaging method is, because that’s like debating which Star Trek movie was the best.
Using your method of packaging, create a package that drops that Custom folder (notice we are dropping the period so it is not hidden) into the following location:
/Library/Application Support/CrashPlan
Now that we’ve got our custom settings, we can move over to the JSS to work on our deployment. I’m going to skip discussing how to do this via Self Service, and instead stick with either a Login trigger or Recurring Check-In trigger. But first things first, go ahead and upload that custom settings package you just created into the JSS. Once it’s uploaded set the priority to something low, like 8:
Create Your Policies
The tech paper discusses uploading the CrashPlan installer along with the custom properties, but I like the method that is discussed in this JAMF Nation post. It’s towards the bottom, and basically it uses curl to download the installer from the CrashPlan server. This method insures you have the latest version for your server. Of course, if you are trying to deploy to end users around the globe that may not have curl access to your CrashPlan server, uploading the installer to Casper may be your only option. For me, however, it was not.
First step is to create a new script in the JSS (or upload a script if your scripts are not stored in the database). The script itself is nothing special, it checks for the presence of the CrashPlan launch daemon, and if it is there unloads it and removes CrashPlan. Then the script continues on to install the custom properties (via a second policy) and finally installs CrashPlan:
#!/bin/sh | |
# unload CrashPlan LaunchDaem if it exists | |
if [[ -e /Library/LaunchDaemons/com.crashplan.engine.plist ]]; then | |
launchctl unload /Library/LaunchDaemons/com.crashplan.engine.plist | |
/Library/Application\ Support/CrashPlan/Uninstall.app/Contents/Resources/uninstall.sh | |
rm -rf /Library/Application\ Support/CrashPlan | |
fi | |
# install the custom properties folder | |
jamf policy -event CrashInstall | |
# now install CrashPlan | |
curl http://yourserveraddress:4280/download/Code42CrashPlan_Mac.dmg > /var/tmp/CP.dmg | |
hdiutil attach /var/tmp/CP.dmg | |
installer -pkg /Volumes/Code42CrashPlan/Install\ Code42\ CrashPlan.pkg -target / | |
hdiutil detach /Volumes/Code42CrashPlan | |
rm -rf /var/tmp/CP.dmg |
As you can see, I’m using a second policy to install the custom properties. You could do everything with one policy and two scripts, or one policy and curl the custom properties from another location. The key point is that if you are removing an existing installation (like I was), you cannot install the custom properties until you are done removing the existing. Make sense?
Now that we have all of our pieces and parts up there, you will create your two policies, one to install the custom properties and the other to run the script.
To Trigger Or Not To Trigger
With your policies created, you now need to determine how you want to trigger these policies. Obviously you will need to trigger one from within the script, but what about the main policy that kicks it all off? Well, I would probably do this via a recurring check-in trigger. It keeps the user from having to wait for the policy to complete before their login completes.
Of course, you could use the login trigger and throw up a nice notification using jamfHelper, Notification Center, or CocoaDialog. That sounds like a nice post for another day.
I Didn’t Do It
I cannot take the credit for this process. It was people like Bob Gendler and Kevin Cecil on JAMF Nation, along with the folks at JAMF and Code42, that did the heavy lifting. I just put it all into one location for me to remember later.
Upgrading CrashPlan Pro to 5.1.2 on Ubuntu
Since I tend to forget things the day after I do them, I’ve decided I’m going to write this one down. While I can manage my way around a LINUX install, especially Ubuntu flavors of Linux, I’m no system administrator or whiz kid by any stretch of the imagination. I tend to plunk around and can find some articles online (usually on Ask Ubuntu forums) to get done what I need. Rather than forget where I found this info, this time I decided I’m going to write it up.
The 5.1.2 upgrade to CrashPlan Pro requires Oracle Java 8 as indicated in this article. Since I run all of my Ubuntu boxes headless (they’re all VMs) I do everything the way any good UNIX head does: via the terminal. Of course, as I stated above, I’m no super user in LINUX so I take the easy way out and use package managers to do the installation, apt-get in particular since I am on Ubuntu. Unfortunately, apt does not have Oracle Java 8 as an available package. So in order for me to be able to use apt-get, I needed to first find a repository that had it.
A quick Google search led me to this article. After following the steps in the article, I had Java 8 installed and ready for the upgrade to version 5.1.2 of CrashPlan.
The next step was to make sure I had a fresh database dump from CrashPlan. Into the GUI for the CPP server to generate a dump. It’s a good idea to copy that dump file off to another server/system.
Now that we’ve backed up and we have Java installed, go ahead and download the update files. You can find the files, along with the official CrashPlan documentation here.
Finally, once you have the server upgraded, you’ll want to upgrade your client devices. You can find the official documentation here.