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.
Meet My New Assistant – Jenkins
Continuing with my posts on automation, or at least the automation of package building, I’d like to introduce you to my new assistant, Jenkins. Jenkins is a continuous integration server. Continuous Integration, or CI, is defined by Wikipedia as:
Continuous integration (CI) is the practice, in software engineering, of merging all developer working copies with a shared mainline several times a day.
To put it plainly, or to put it into the perspective of a Mac sysadmin, Jenkins can be used to run shell commands at a given interval, or to run several commands in series. We can then use CRON like scheduling within Jenkins to run these commands at a given time each day, each week, or whatever interval you give.
Jenkins will require the use of the Java 6 JRE on the machine you run it on. You can go out to the Jenkins CI web site (http://jenkins-ci.org/) to download the installer for Mac. Once you have downloaded the software, while logged in as an admin on the machine, run the Jenkins installer PKG. After the installation has completed, either restart the computer, or open a Terminal window and load the LaunchDaemon:
launchctl load /Library/LaunchDaemon/org.jenkins-ci.plist
If Java is not installed you may be prompted to load Java. After completing these steps, open a web browser and navigate to the home page for Jenkins:
You should hopefully see the Jenkins dashboard, which looks similar to this:
NOTE: If you receive an error about the home folder, try restarting the computer.
You can also check this web site for more info: https://wiki.jenkins-ci.org/display/JENKINS/Thanks+for+using+OSX+Installer
If you successfully loaded the Jenkins dashboard, then you are all set to start using the power of a CI server. I utilize Jenkins to work with my AutoPkg installation (on the same machine) so that I can build packages while I sleep.
Next up we’ll look at integrating Jenkins and AutoPkg.
AutoPkg – Your New Best Friend
There’s been a lot of talk in the Mac admin community recently about AutoPkg (http://autopkg.github.io/autopkg/). This tool, currently being developed by Greg Neagle and Tim Sutton, allows for the use of “recipes” to download and package different software found on the Internet. This ability takes a lot of the work out of locating software, downloading it, and then packaging it, something a Mac admin may do multiple times a week. AutoPkg automates all of that. And, if you are a user of Munki, there are even recipes in AutoPkg that will import directly into Munki and update the catalogs.
As a prerequisite for AutoPKG to work, you need to have the command line version of git installed. If you’ve installed XCode, then you have git. If not, you can get git by downloading either a GUI GitHub tool like this one here, or download the command line version of git here.
Once you have git installed, go ahead and download the AutoPkg installer file from this location. Now that it is downloaded, go ahead and run the AutoPkg installer. This will install the AutoPkg software in the proper locations on the machine. Once completed, fire up Terminal and you can verify the software is properly installed by running the following command:
autopkg version
That will give you the current version installed on the system. Now that AutoPkg is installed, you may want to create a directory in a common location, like the root of the drive or in the /Users/Shared folder, to hold the output from AutoPKG. By default AutoPkg stores it’s output inside the user’s Library folder at ~/Library/AutoPkg/Cache. This location can be changed by adjusting the CACHE_DIR in the preferences. I like to store it at the root of the drive in a folder named Autopkg_Done.
mkdir /Autopkg_Done chmod 777 /Autopkg_Done
Now that you have the CACHE_DIR created, you’ll need to tell AutoPkg about it. This is done with the following command:
defaults write com.github.autopkg CACHE_DIR /path/to/cache/dir
So, in our example, the command we’d use is this:
defaults write com.github.autopkg CACHE_DIR /Autopkg_DoneNOTE: AutoPKG stores its preferences in the user’s Preferences folder (~/Library/Preferences/com.github.autopkg.plist). Because of this, any new user on the machine that will need to use AutoPKG will need to have the CACHE_DIR set and will need to download the recipe repositories (more on this in a minute).
With AutoPkg installed, and the CACHE_DIR set, all we have left is to tell it about some recipe repositories. AutoPkg utilizes git repositories that hold recipes for building different packages. Like using a recipe to bake a cake, AutoPkg uses these recipes to build the different files.
As of this writing, there are four repositories that I am aware of. They are:
https://github.com/Jaharmi/autopkg_recipes
http://github.com/autopkg/recipes.git
https://github.com/hjuutilainen/autopkg-recipes
http://github.com/keeleysam/recipes
We need to tell AutoPkg about these repositories. This is accomplished with the following command line:
autopkg repo-add <repoURL>
So for the four repositories above, we would use the following:
autopkg repo-add https://github.com/Jaharmi/autopkg_recipes autopkg repo-add http://github.com/autopkg/recipes.git autopkg repo-add https://github.com/hjuutilainen/autopkg-recipes autopkg repo-add http://github.com/keeleysam/recipes
Now that we have our repositories in AutoPkg, we are free to run one of the recipes. We can see what recipes we have access to with the following:
autopkg list-recipes
If all went right, you should see a list of the recipes that your AutoPkg installation knows about. To run one, simply use the following:
autopkg run <recipe>
For example, to run Adobe Flash Player:
autopkg run AdobeFlashPlayer.pkg
That will download the Flash Player and create a PKG file for deployment inside of our CACHE_DIR (/Autopkg_Done in our case). We can navigate through the folders there to find a pristine Flash installer package that we can now deploy to our machines.
There’s plenty more to know about AutoPkg, including how to import directly into Munki during a build. Check out the wiki for more information on Munki integration, or if you want help with installation and config, go here.
Next up, we’ll setup Jenkins, a continuous integration server that will eventually allow us to build packages while we sleep