Viewing Mac App Store Purchases From The Command Line

As you may have noticed, we’ve been working on building some links between the App Store and patch management tools such as Casper, FileWave and Munki. We’ve been looking at policy-based management of apps as well. In this semi-new world of signing and stores and the such, there’s actually a good bit you can ascertain about an app both inside the app as well as inside metadata OS X keeps about the app. I’ve discussed signing (apps and packages) in the past, but let’s look at using some commands to help us out with some tasks. The first command is to determine some information about apps that are on the computer. Spotlight keeps a fair amount of information about these apps and can be invoked using the mdls command. Running the command with no additional parameters looks like this (I’m gonna’ use iMovie in these examples, although note that there are spaces in a lot of app names and paths as you start scripting things – so use IFS rather than trying to use traditional array): mdls /Applications/ This results in output similar to the following (I’ve stripped out a few fields as they consume a lot of space and aren’t super pertinent to what I’m trying to do here):
kMDItemAlternateNames = ( "" ) kMDItemAppStoreCategory = "Video" kMDItemAppStoreCategoryType = "" kMDItemCFBundleIdentifier = "" kMDItemContentCreationDate = 2011-09-28 08:04:34 +0000 kMDItemContentModificationDate = 2012-09-22 02:13:45 +0000 kMDItemContentType = "" kMDItemDisplayName = "iMovie" kMDItemExecutableArchitectures = ( i386 ) kMDItemFSContentChangeDate = 2012-09-22 02:13:45 +0000 kMDItemFSCreationDate = 2011-09-28 08:04:34 +0000 kMDItemVersion = "9.0.8"
To just ask for one of these attributes, run the command along with the -name option in addition to the metadata attribute you’d like returned. For example, to see the bundle ID (kMDItemCFBundleIdentifier), use: mdls /Applications/ -name kMDItemCFBundleIdentifier /Applications/ Now, if you’d like to just quickly ascertain what apps on the system came from the App Store, use the mdfind command, along with whatever of the attributes matches what you want to know. Running mdfind for kMDItemAppStoreHasReceipt of 1 would look like the following and would result in a list of all apps on the system that came from the App Store: mdfind kMDItemAppStoreHasReceipt=1 Blacklisting all apps that are part of a specific category (and with regard to customer requests, that category seems to always be Games) is something we get a lot of banter about with customers. To determine this information for apps, you can run mdfind on kMDItemAppStoreCategory for Games: mdfind kMDItemAppStoreCategory=Games You could then dump the contents of those into something that can blacklist apps (or whitelist based on other categories). Now, version control is another hot topic at various organizations. To see the version type of a given app, use the -name option with mdls kMDItemVersion mdls /Applications/ -name kMDItemVersion /Applications/ Then you can track the version of the app and take action through other ways to remove old versions and force users to upgrade. The mdfind command can also be leveraged to find apps that have escaped their traditional homes of /Applications and /Applications/Utilities, with the ability to obtain a full list by querying for kMDItemContentType of app bundles, as follows: mdfind kMDItemContentType="" Loading a list of apps (output from `mdfind kMDItemAppStoreHasReceipt=1` or `mdfind kMDItemAppStoreCategory=Games`) into an array and then querying each one of them for more information is pretty trivial beyond the steps we’ve already taken. This information can then be fed into some kind of Managed Prefs script to deny or allow access to various objects or an admin could even chmod the bundle, mark it as invisible, poison it (keep in mind, if you alter it you’ll break the signing), etc in order to get some desired outcome. You can also use defaults to read a users file for the AppleID field to see what AppleID is currently logged into the AppStore, providing another variable that can be reported on: defaults read /Users/cedge/Library/Preferences/ AppleID And yes, it’s worth noting that users from another account or a system image, etc can be used to download apps so this one isn’t exactly certain but the purchaser isn’t stored anywhere within the bundle nor is it permissioned in a way that we can use to find the purchaser that way. There’s still a bit of a gap right now with regards to some of these technologies that Mac SysAdmins are managing. The consumeristic technologies such as App Stores are here to stay. We’re kidding ourselves if we think that we won’t be able to buy certain apps via Volume Licenses and have pkg installers for too much longer. Apple has made no indication that they’re dropping the results that can be obtained with a simple installer command, but with forcing signing on certain objects, gatekeeper and other technologies it’s hard to say what the future will really have in store for us. Getting to a point where we can report on elements of the App Store and hopefully eventually deploy objects through the App Store should continue to help bridge these factors, but I still see the need for additional binaries from Apple to be introduced to get the rest of the way there (or at least expose a method to me so I can go in there and buy an app through the method).

Managing Mail and Safari RSS Subscriptions from the Command Line

Safari can subscribe to RSS feeds; so can Mail. Podcast Producer is an RSS or XML feed as are the feeds created by blog and wiki services in Mac OS X Server. And then of course, RSS and ATOM come pre-installed with practically every blogging and wiki tool on the market. Those doing mass deployment and scripting work can make use of automatically connecting users to and caching information found in these RSS feeds. If you have 40,000 students, or even 250 employees, it is easier to send a script to those computers than to open the Mail or Safari client on each and subscribe to an RSS feed. Additionally, pubsub offers what I like to call Yet Another Scripting Interface to RSS (my acronym here is meant to sound a bit like yessir).  Pubsub caches the feeds both within the SQLite database and in the form of XML files. Because pubsub caches data onto the client it can be parsed more quickly than using other tools, allowing a single system to do much more than if a feed were being accessed over the Internet. Using pubsub We’ll start by looking at some simple RSS management from the command line to aid in a quest at better understanding of the underpinnings of Mac OS X’s built-in RSS functionalities. The PubSub framework stores feeds and associated content in a SQLite database. Interacting with the database directly can be a bit burdensome. The easiest way to manage RSS from Mac OS X is using a command called pubsub. First off, let’s take a look at all of the RSS feeds that the current user is subscribed to by opening terminal and simply typing pubsub followed by the list verb: pubsub list You should then see output of the title and url of each RSS feed that mail and safari are subscribed to. You’ll also see how long each article is kept in the expiry option and the interval with which the applications check for further updates in the refresh option. You can also see each application that can be managed with pubsub by running the same command with clients appended to the end of it (clients are how pubsub refers to applications whose subscriptions it can manage): pubsub list clients To then just look at only feeds in Safari: pubsub list client And Mail: pubsub list client Each of the above commands will provide a URL for the feed. This url can be used to show each entry, or article in the feed. Extract the URL and then you can use the list verb to see each feed entry, which Apple consistently calls episodes both within PubSub, in databases and on the Podcast Producer server side of things but yet somehow calls an entry here (consistency people). To see a list of entries for a given URL: pubsub list Episodes will be listed in 40 character hex keys, similar to other ID space mechanisms used by Apple. To then see each episode, or entry, use the list verb, followed by entry and then that key: pubsub list entry 5fcef167d77c8c00d7ff041a869d45445cc4ae42 To subscribe to a pubsub, use the –client option to identify which application to subscribe in along with the subscribe verb, followed by the URL of the feed: pubsub --client subscribe To unsubscribe, simply use pubsub followed by the unsubscribe verb and then the url of the feed: pubsub unsubscribe Ofline Databases and Imaging While these can be run against a typical running system, they cannot be run against a sqlite database that is sitting in all of your users home folders nor can they be run against a database in a user template home on a client. Therefore, to facilitate imaging, you can run sqlite3 commands against  database directly. The database, stored in ~/Library/PubSub/Database/Database.sqlite3. To see the clients (the equivalent of `pubsub list clients`): sqlite3 /Volumes/Image/Username/Library/PubSub/Database/Database.sqlite3 'SELECT * FROM clients' To see each feed: sqlite3 /Volumes/Image/Username/Library/PubSub/Database/Database.sqlite3 'SELECT * FROM feeds' To see each entry: sqlite3 /Volumes/Image/Username/Library/PubSub/Database/Database.sqlite3 'SELECT * FROM entries' To see the column headers for each: sqlite3 /Volumes/Image/Username/Library/PubSub/Database/Database.sqlite3 'PRAGMA TABLE_INFO(Clients)'; sqlite3 /Volumes/Image/Username/Library/PubSub/Database/Database.sqlite3 'PRAGMA TABLE_INFO(Feeds)'; sqlite3 /Volumes/Image/Username/Library/PubSub/Database/Database.sqlite3 'PRAGMA TABLE_INFO(Subscriptions)'; sqlite3 /Volumes/Image/Username/Library/PubSub/Database/Database.sqlite3 'PRAGMA TABLE_INFO(Entries)'; sqlite3 /Volumes/Image/Username/Library/PubSub/Database/Database.sqlite3 'PRAGMA TABLE_INFO(Enclosures)'; sqlite3 /Volumes/Image/Username/Library/PubSub/Database/Database.sqlite3 'PRAGMA TABLE_INFO(Authors)'; sqlite3 /Volumes/Image/Username/Library/PubSub/Database/Database.sqlite3 'PRAGMA TABLE_INFO(Contents)';     sqlite3 /Volumes/Image/Username/Library/PubSub/Database/Database.sqlite3 'PRAGMA TABLE_INFO(SyncInfo)'; To narrow an ID down to a specific row within any of these searches add a WHERE followed by the column within the table you’d like to search. For example, if we wanted to only see the article with the identifier of 5b84e609317fb3fb77011c2d26efd26a337d5d7d sqlite3 --line /Volumes/Image/Username/Library/PubSub/Database/Database.sqlite3 'SELECT * FROM entries WHERE identifier="5b84e609317fb3fb77011c2d26efd26a337d5d7d"' Note: Sqlite3 can use the –line option to show each entry in an XML feed per line. Dumping pubsub to be Parsed By Other Tools Pubsub can also be used as a tool to supply feeds and parse them. You can extract conversations only matching specific patterns and text or email yourself that they occurred without a lot of fanfare. You can also dump the entire feed’s cached data by specifying the dump verb without the entry or identifier but instead the URL: pubsub dump Once dumped you can parse the XML into other tools easily. Or to dump specific entries to XML for parsing by another tool using syntax similar to the list entry syntax: pubsub dump entry 5fcef167d77c8c00d7ff041a869d45445cc4ae42 Because these feeds have already been cached on the local client and because some require authentication and other expensive (in terms of script run-time) processes to aggregate or search, looking at the files is an alternative way of doing so. Instant refreshes can also be performed using pubsub’s refresh verb followed by a URL: pubsub refresh Also, feeds are cached to ~/Library/PubSub/Feeds, where they are nested within a folder with the name of the unique ID of the feed (row 2 represents the unique ID whereas row 1 represents the row). Each episode, or post can then be read by entry ID. Yhose entries are basic xml files. You can also still programatically interface with RSS using curl. For example: curl --silent "http://${server}" | grep "item rdf:about=" | cut -c 18-100 | sed -e "s/"//g" | sed -e "s/>//g"

Deploy CrashPlan Pro en Masse

Recently, I looked at leveraging the CrashPlan Pro REST API to extend the functionality of what can be done with CrashPlan Pro. It just so happens that I’m not quite done talking about CrashPlan Pro just yet. Now that you are off to the races with the API, it’s time to look at pushing that client you’ve got out en masse. Our good friends at JAMF software have been kind enough to publish a white paper that is pretty darn awesome to get ya’ started. But let’s look at what, other than setting the server address and registration key, that you can do. There are a few files embedded in the installer package. Most importantly, these include a default.service.xml file and a The looks at the system the package is installing on and figures out some variables to populate the default.service.xml file with. The default.service.xml file ends up with some other settings (ie – host name, registration key, etc) that are static in the file rather than dynamically loaded based on the output of the To get started building your deployment package, open the Install CrashPlanPRO installer package and then copy the Custom-example folder, naming the new instance simply Custom (or rename Custom-example to Custom given that you can always redownload). Then find the commented out section for <!– <authority address=”hostname:port”/> –> and uncomment it, changing the address variable so that the hostname:port reflects the actual address and port combination of your server. For example, if your server name were and you were running CrashPlan Pro on the default port (4282) then the address to use would be File System Layout of the Package You also need to choose whether or not the server address will be shown to users, the registration key of the organization that your user will be placed into and choose how to handle the username and password. These are done using the hideAddress, RegistrationKey, username and password variables. If you wish to hide the server address from end users, once the package has been deployed, then the hideAddress variable would be true; otherwise it would be false. Each organization in your server will have a registration key. Use the quoted form of the registration key for the organization (or parent organization) that the package should join in the registrationKey field. If the username is to be dynamically loaded from the then you should leave username=”${username}” and the password can be set to deferred or statically assigned as might be common with new deployments. If this seems like a lot, the authority section should read as follows:
<authority address=”” hideAddress=”true” registrationKey=”ABCD-EFGH-IJKL-MNOP” username=”${username}” password=”${deferred}” />
The above would suit most deployments. But the real flexibility comes in with Given that is a shell script, you can go way beyond what CrashPlan intends. By default, the uncommented lines of the script include the following:
startDesktop=true userHome=”$HOME” user=”$USER” userGroup=`id -gn “$user”` userEmail=”$user”
The startDesktop variable defines whether the desktop application will open following the installation of the package. The $USER variable gives you the opportunity to build out the user that will be used based on a number of variables. One of which is the computername for those environments that use the user name in LDAP as the computer name. For example, a line to set a username might instead be:
user=`scutil –get ComputerName`.
Or the user name might be pulled in from a user fillable field in Apple Remote Desktop:
user=`defaults read /Library/Preferences/ Text1`
The reason for the focus on pulling the user name out of something other than the currently logged in user here is that many will want to push out a lot of computers en masse without logging in as the user who will get the systems for each one… Of course, user isn’t the only thing we’ll want… The userGroup variable doesn’t seem to do anything, so we’re not going to be worried about that one. But the userEmail can often be looked up using dscl based on whatever LDAP attribute you might be storing it in… Oh, and then there’s the userHome. Well, what exactly do you want to back up on the machine? Just the home or /Users or /Volumes/MyBigOldXsan? One very interesting aspect of the script is that you can kill the package at this point and throw an error. For example, if the server is not accessible then the installer will complete but the package will not be useable. he banner for port 4282 isn’t easily programatically checked, although performing a quick check of the port is a good start for this (and becomes one of the ways you can monitor that CrashPlan Pro is running):
“`/Applications/Utilities/Network 4282 4282 | awk ‘{print $1}’`”
If the port isn’t open then let’s just:
else killall Installer fi
Or something like that… If you’re going to be pushing a package out then it’s not very likely that you’re going to worry too much about customizing how the installer actually looks. But, if you’re going to allow users to install it then you might want to. Basically, there are a number of images in a directory in your new custom folder called skin. In here, you can edit the images that make up those that appear throughout the package when it is run manually.

VMware Fusion 3 Right Around the Corner

VMware Fusion is now in pre-order for Fusion 3. What’s new?
  • 64-bit support for Snow Leopard systems
  • Support for OpenGL 2.1 and DirectX 9.0c
  • Unity works with Aero and Flip 3D to further support Windows 7
  • New migration tools
  • New command line options
  • Upgraded Virtual Machine Library
  • Built-in Automatic Updater
  • Reduced memory and CPU footprint
Additionally, it looks like we’ll be updating the mass deployment guide so if you have any updates or points of information that should be added, let us know!

Mass Deploy Parallels

Sometimes it’s just that easy. Our good friends at Parallels have developed a special Mass Deploy package, available on their site. When you control-click on it and select Browse Contents you will see a license.txt.  You can paste your license into the license.txt file and then put your virtual machine into the root of the package. Parallels Mass Deploy, Mac OS X Once complete, you can push this package out at will.  Additionally, you can edit the postflight shell script in the Resources directory, throwing your own commands at the tail end of the file, adding more virtual machines, customizing settings, etc.  Good luck.