Tiny Deathstars of Foulness

There’s another new conference in town! Well, not my town, but Vancouver. MacDev Ops is a hot topic. One that will only increase in the coming years. Thanks to Mat X and Brian Warsing for bringing about a brilliant conference. Screen Shot 2015-03-23 at 10.43.50 PM The conference will be held on June 19, 2015 and is an easy $99 if you sign up soon. Also, submit a talk if DevOps is your thing. They’re looking to bring the following topics to the table:
  • Puppet, Chef and other automation from Desktop to Cloud and back
  • Software deployment with Munki and AutoPkg: the app ecosystem surrounding it
  • Cool tools: demo of awesome Mac Admin projects from GitHub
  • DevOps: How to adopt Automation and Best practices in IT operations
  • Dev skills: workshops on Ruby, Git, Python, Javascript for Mac Admins
  • MDM: Profiles and Mac configuration management in the cloud
This is sure to be a good one. Check it out here!

March 23rd, 2015

Posted In: Mac OS X, Programming, Unix

Tags: , , , , , , , , , ,

Merry Christmas ya’ll!
On the first day of Christmas my true love gave to me one 32 gig iPad On the second day of Christmas my true love gave to me two bash one-liners On the third day of Christmas my true love gave to me three Red Hat servers On the fourth day of Christmas my true love gave to me four email blasts On the fifth day of Christmas my true love gave to me five retweets On the sixth day of Christmas my true love gave to me six regular expressions On the seventh day of Christmas my true love gave to me seven lines of perl On the eighth day of Christmas my true love gave to me eight app store apps On the ninth day of Christmas my true love gave to me nine AWS instances On the tenth day of Christmas my true love gave to me ten Active Directory forests On the eleventh day of Christmas my true love gave to me 11 crappy python scripts On the twelfth day of Christmas my true love gave to me 12 craft brews

December 25th, 2014

Posted In: iPhone, Mac OS X, Mac OS X Server, Mac Security, Mass Deployment

Tags: , , , , , , , , , , , , , ,

JSS-autopkg-addon Presentation from Allister Banks on Vimeo. (Guest post by Allister Banks) On June 26th, I had the pleasure of being invited by @Tecnico1931 to the NYC Metro JAMF user group meeting. A worksheet I created for this event may be found here: See also Shea Craig’s python-jss, and thanks go out to James Barclay, Sam Johnson, and all the folks mentioned in the video.

July 1st, 2014

Posted In: Mac OS X, Mac OS X Server

Tags: , , , , , ,

Microsoft Azure is Microsoft’s cloud services. Azure can host virtual machines and act as a location to store files. However, Azure can do much more as well, providing an Active Directory instance, provide SQL database access, work with hosted Visual Studio, host web sites or provide BizTalk services. All of these can be managed at windows_azure_logo6 You can also manage Windows Azure from the command line on Linux, Windows or Mac. To download command line tools, visit Once downloaded, run the package installer. Screen Shot 2013-11-29 at 10.51.01 PMWhen the package is finished installing, visit /usr/local/bin where you’ll find the azure binary. Once installed, you’ll need to configure your account from the site to work with your computer. To do so, log into the portal. Screen Shot 2013-12-01 at 8.25.57 PM Once logged in, open Terminal and then use the azure command along with the account option and the download verb: azure account download This account downloads the .publishsettings file for the account you’re logged in as in your browser. Once downloaded, run azure with the account option and the import verb, dragging the path to your .publishsettings file from azure account import /Users/krypted/Downloads/WindowsAzure-credentials.publishsettings The account import then completes and your user is imported into azure. Once imported, run azure with the account option and then storage list: azure account storage list You might not have any storage configured yet, but at this point you should see the following to indicate that the account is working: info: No storage accounts defined info: account storage list command OK You can also run the azure command by itself to see some neat ascii-art (although the azure logo doesn’t really come through in this spiffy cut and paste job): info: _ _____ _ ___ ___________________ info:        /_\  |__ / | | | _ \ __| info: _ ___ / _ \__/ /| |_| |   / _|___ _ _ info: (___ /_/ \_\/___|\___/|_|_\___| _____) info: (_______ _ _) _ ______ _)_ _ info: (______________ _ ) (___ _ _) info: info: Windows Azure: Microsoft's Cloud Platform info: info: Tool version 0.7.4 help: help: Display help for a given command help: help [options] [command] help: help: Open the portal in a browser help: portal [options] help: help: Commands: help: account to manage your account information and publish settings help: config Commands to manage your local settings help: hdinsight Commands to manage your HDInsight accounts help: mobile Commands to manage your Mobile Services help: network Commands to manage your Networks help: sb Commands to manage your Service Bus configuration help: service Commands to manage your Cloud Services help: site Commands to manage your Web Sites help: sql Commands to manage your SQL Server accounts help: storage Commands to manage your Storage objects help: vm Commands to manage your Virtual Machines help: help: Options: help: -h, --help output usage information help: -v, --version output the application version Provided the account is working, you can then use the account, config, hdinsight, mobile, network, sb, service, site, sql, storage or vm options. Each of these can be invoked along with a -h option to show a help page. For example, to see a help page for service: azure service -h You can spin up resources including sites, storage containers and even virtual machines (although you might need to create templates for VMs first). As an example, let’s create a new site using the git template: azure site create --git Overall, there are a lot of options available in the azure command line interface. The web interface is very simple, with options in the command line interface mirroring the options in the web interface. Running and therefore scripting around these commands is straight forward. I wrote up some Amazon stuff previously at, but the azure controls are really full featured and I’m really becoming a huge fan of the service itself the more I use it (which likely means I’ll post more articles on it soon).

December 2nd, 2013

Posted In: cloud, Network Infrastructure, SQL, Ubuntu, Unix, VMware, Windows Server

Tags: , , , , , , , , , , ,

Configuring web services is as easy in OS X Mountain Lion Server (10.8) as it has ever been. To set up the default web portal, simply open the Server app, click on the Websites service and click on the ON button. After a time, the service will start. Once running, click on the View Server Website link at the bottom of the pane. Provided the stock OS X Server page loads, you are ready to use OS X Server as a web server. Before we setup custom sites, there are a few things you should know. The first is, the server is no longer really designed to remove the default website. So if you remove the site, your server will exhibit inconsistent behavior. Also, don’t remove the files that comprise the default site. Instead just add sites, which is covered next. Webmail is gone. You don’t have to spend a ton of time looking for it as it isn’t there. Also, Mountain Lion Server adds web apps, which we’ll briefly review later in this article as well.  Finally, enabling PHP and Python on sites is done globally, so this setting applies to all sites hosted on the server. Now that we’ve got that out of the way, let’s add our first custom site. Do so by clicking on the plus sign. At the New Web Site pane, you’ll be prompted for a number of options. The most important is the name of the site, with other options including the following:
  • Domain Name: The name the site is accessible from. The default sites do not have this option as they are accessible from all names that resolve to the server.
  • IP Address: The IP address the site listens on. Any means the site is available from every IP address the server is configured to use. The default websites do not have this option as they are accessible from all addresses automatically
  • Port: By default, sites without SSL run on port 80 on all network interfaces, and sites with SSL run on port 443 on all network interfaces. Use the Port field to use custom ports (e.g., 8080). The default sites do not have this option as they are configured to use 80 and 443 for default and SSL-based communications respectively.
  • SSL Certificate: Loads a list of SSL certificates installed using Keychain or the SSL Certificate option in the Settings pane of the Server application
  • Store Site Files In: The directory that the files that comprise the website are stored in. These can be placed into the correct directory using file shares or copying using the Finder. Click on the drop-down menu and then select Other to browse to the directory files are stored in.
  • Who Can Access: By default Anyone (all users, including unauthenticated guests) can access the contents of sites. Clicking on Anyone and then Customize… brings up the “Restrict access to the following folders to a chosen group” screen, where you can choose web directories and then define groups of users who can access the contents.
  • Additional Domains: Click on the Edit… button to bring up a simple list of domain names the the site also responds for (e.g. in addition to, add
  • Redirects: Click on the Edit… button to bring up a list of redirects within the site. This allows configuring redirects to other sites. For example, use /en to load or /cn to load
  • Aliases: Click on the Edit… button to load a list of aliases. This allows configuring redirects to folders within the same server. For example, /en loads /Library/Server/Web/Data/Sites/Default
  • Index Files: Click on the Edit… button to bring up a list of pages that are loaded when a page isn’t directly indicated. For example, when visiting, load the wp.php page by default.
  • Advanced Options: The remaining options are available by clicking on the “Edit Advanced Settings…” button.
  • Enable Server Side Includes: Allows administrators to configure leveraging includes in web files, so that pieces of code can be used across multiple pages in sites.
  • Allow overrides using .htaccess files: Using a .htaccess file allows administrators to define who is able to access a given directory, defining custom user names and passwords in the hidden .htaccess file. These aren’t usually required in an OS X Server web environment as local and directory-based accounts can be used for such operations. This setting enables using custom .htaccess files instead of relying on Apple’s stock web permissions.
  • Allow folder listing: Enables folder listings on directories of a site that don’t have an Index File (described in the non-Advanced settings earlier).
  • Allow CGI execution: Enables CGI scripts for the domain being configured.
  • Use custom error page: Allows administrators to define custom error pages, such as those annoying 404 error pages that load when a page can’t be found
  • Make these web apps available on this website: A somewhat advanced setting, loads items into the webapps array, which can be viewed using the following command:  sudo serveradmin settings web:definedWebApps
Once you’ve configured all the appropriate options, click on Done to save your changes. The site should then load. Sites are then listed in the list of Websites. The Apache service is most easily managed from the Server app, but there are too many options in Apache to really be able to put into a holistic graphical interface. The easiest way to manage the Websites service in OS X Mountain Lion server is using the serveradmin command. Apache administrators from other platforms will be tempted to use the apachectl command to restart the Websites service. Instead, use the serveradmin command to do so. To start the service: sudo serveradmin start web To stop the service(s): sudo serveradmin stop web And to see the status: sudo serveradmin fullstatus web Fullstatus returns the following information: web:health = _empty_dictionary web:readWriteSettingsVersion = 1 web:apacheVersion = "2.2" web:servicePortsRestrictionInfo = _empty_array web:startedTime = "2012-08-13 23:01:42 +0000" web:apacheState = "RUNNING" web:statusMessage = "" web:ApacheMode = 2 web:servicePortsAreRestricted = "NO" web:state = "RUNNING" web:setStateVersion = 1 While the health option typically resembles kiosk computers in the Computer Science departments of most major universities, much of the rest of the output can be pretty helpful including the Apache version, whether the service is running, any restrictions on ports and the date/time stamp that the service was started. To see all of the settings available to the serveradmin command, run it, followed by settings and then web, to indicate the Websites service: sudo serveradmin settings web The output is pretty verbose and can be considered in two sections, the first includes global settings across sites as well as the information for the default sites that should not be deleted: web:defaultSite:documentRoot = "/Library/Server/Web/Data/Sites/Default" web:defaultSite:serverName = "" web:defaultSite:realms = _empty_dictionary web:defaultSite:redirects = _empty_array web:defaultSite:enableServerSideIncludes = no web:defaultSite:customLogPath = "&quot;/var/log/apache2/access_log&quot;" web:defaultSite:webApps = _empty_array web:defaultSite:sslCertificateIdentifier = "" web:defaultSite:fullSiteRedirectToOtherSite = "" web:defaultSite:allowFolderListing = no web:defaultSite:serverAliases = _empty_array web:defaultSite:errorLogPath = "&quot;/var/log/apache2/error_log&quot;" web:defaultSite:fileName = "/Library/Server/Web/Config/apache2/sites/0000_any_80_.conf" web:defaultSite:aliases = _empty_array web:defaultSite:directoryIndexes:_array_index:0 = "index.html" web:defaultSite:directoryIndexes:_array_index:1 = "index.php" web:defaultSite:directoryIndexes:_array_index:2 = "/wiki/" web:defaultSite:directoryIndexes:_array_index:3 = "default.html" web:defaultSite:allowAllOverrides = no web:defaultSite:identifier = "37502141" web:defaultSite:port = 80 web:defaultSite:allowCGIExecution = no web:defaultSite:serverAddress = "*" web:defaultSite:requiresSSL = no web:defaultSite:proxies = _empty_dictionary web:defaultSite:errorDocuments = _empty_dictionary web:defaultSecureSite:documentRoot = "/Library/Server/Web/Data/Sites/Default" web:defaultSecureSite:serverName = "" web:defaultSecureSite:realms = _empty_dictionary web:defaultSecureSite:redirects = _empty_array web:defaultSecureSite:enableServerSideIncludes = no web:defaultSecureSite:customLogPath = "&quot;/var/log/apache2/access_log&quot;" web:defaultSecureSite:webApps = _empty_array web:defaultSecureSite:sslCertificateIdentifier = "" web:defaultSecureSite:fullSiteRedirectToOtherSite = "" web:defaultSecureSite:allowFolderListing = no web:defaultSecureSite:serverAliases = _empty_array web:defaultSecureSite:errorLogPath = "&quot;/var/log/apache2/error_log&quot;" web:defaultSecureSite:fileName = "/Library/Server/Web/Config/apache2/sites/0000_any_443_.conf" web:defaultSecureSite:aliases = _empty_array web:defaultSecureSite:directoryIndexes:_array_index:0 = "index.html" web:defaultSecureSite:directoryIndexes:_array_index:1 = "index.php" web:defaultSecureSite:directoryIndexes:_array_index:2 = "/wiki/" web:defaultSecureSite:directoryIndexes:_array_index:3 = "default.html" web:defaultSecureSite:allowAllOverrides = no web:defaultSecureSite:identifier = "37502140" web:defaultSecureSite:port = 443 web:defaultSecureSite:allowCGIExecution = no web:defaultSecureSite:serverAddress = "*" web:defaultSecureSite:requiresSSL = yes web:defaultSecureSite:proxies = _empty_dictionary web:defaultSecureSite:errorDocuments = _empty_dictionary web:dataLocation = "/Library/Server/Web/Data" web:mainHost:keepAliveTimeout = 15.000000 web:mainHost:maxClients = "50%" The second section is per-site settings, with an array entry for each site: web:customSites:_array_index:0:documentRoot = "/Library/Server/Web/Data/Sites/" web:customSites:_array_index:0:serverName = "" web:customSites:_array_index:0:realms = _empty_dictionary web:customSites:_array_index:0:redirects = _empty_array web:customSites:_array_index:0:enableServerSideIncludes = no web:customSites:_array_index:0:customLogPath = "/var/log/apache2/access_log" web:customSites:_array_index:0:webApps = _empty_array web:customSites:_array_index:0:sslCertificateIdentifier = "" web:customSites:_array_index:0:fullSiteRedirectToOtherSite = "" web:customSites:_array_index:0:allowFolderListing = no web:customSites:_array_index:0:serverAliases = _empty_array web:customSites:_array_index:0:errorLogPath = "/var/log/apache2/error_log" web:customSites:_array_index:0:fileName = "/Library/Server/Web/Config/apache2/sites/" web:customSites:_array_index:0:aliases = _empty_array web:customSites:_array_index:0:directoryIndexes:_array_index:0 = "index.html" web:customSites:_array_index:0:directoryIndexes:_array_index:1 = "index.php" web:customSites:_array_index:0:directoryIndexes:_array_index:2 = "/wiki/" web:customSites:_array_index:0:directoryIndexes:_array_index:3 = "default.html" web:customSites:_array_index:0:allowAllOverrides = no web:customSites:_array_index:0:identifier = "41179886" web:customSites:_array_index:0:port = 80 web:customSites:_array_index:0:allowCGIExecution = no web:customSites:_array_index:0:serverAddress = "*" web:customSites:_array_index:0:requiresSSL = no web:customSites:_array_index:0:proxies = _empty_dictionary web:customSites:_array_index:0:errorDocuments = _empty_dictionary The final section (the largest by far) includes array entries for each defined web app. The following shows the entry for a Hello World Python app: web:definedWebApps:_array_index:15:requiredWebAppNames = _empty_array web:definedWebApps:_array_index:15:includeFiles:_array_index:0 = "/Library/Server/Web/Config/apache2/httpd_wsgi.conf" web:definedWebApps:_array_index:15:requiredModuleNames:_array_index:0 = "wsgi_module" web:definedWebApps:_array_index:15:startCommand = "" web:definedWebApps:_array_index:15:sslPolicy = 0 web:definedWebApps:_array_index:15:requiresSSL = no web:definedWebApps:_array_index:15:requiredByWebAppNames = _empty_array web:definedWebApps:_array_index:15:launchKeys = _empty_array web:definedWebApps:_array_index:15:proxies = _empty_dictionary web:definedWebApps:_array_index:15:preflightCommand = "" web:definedWebApps:_array_index:15:stopCommand = "" web:definedWebApps:_array_index:15:name = "" web:definedWebApps:_array_index:15:displayName = "Python &quot;Hello World&quot; app at /wsgi" Each site has its own configuration file defined in the array for each section. By default these are stored in the /Library/Server/Web/Config/apache2/sites directory, with /Library/Server/Web/Config/apache2/sites/ being the file for the custom site we created previously. As you can see, many of the options available in the Server app are also available in these files: <VirtualHost *:80> ServerName ServerAdmin DocumentRoot "/Library/Server/Web/Data/Sites/" DirectoryIndex index.html index.php /wiki/ default.html CustomLog /var/log/apache2/access_log combinedvhost ErrorLog /var/log/apache2/error_log <IfModule mod_ssl.c> SSLEngine Off SSLCipherSuite "ALL:!aNULL:!ADH:!eNULL:!LOW:!EXP:RC4+RSA:+HIGH:+MEDIUM" SSLProtocol -ALL +SSLv3 +TLSv1 SSLProxyEngine On SSLProxyProtocol -ALL +SSLv3 +TLSv1 </IfModule> <Directory "/Library/Server/Web/Data/Sites/"> Options All -Indexes -ExecCGI -Includes +MultiViews AllowOverride None <IfModule mod_dav.c> DAV Off </IfModule> <IfDefine !WEBSERVICE_ON> Deny from all ErrorDocument 403 /customerror/websitesoff403.html </IfDefine> </Directory> </VirtualHost> The serveradmin command can also be used to run commands. For example, to reset the service to factory defaults, delete the configuration files for each site and then run the following command: sudo serveradmin command web:command=restoreFactorySettings The final tip I’m going to give in this article is when to make changes with each app. I strongly recommend making all of your changes in the Server app when possible. When it isn’t, use serveradmin and when you can’t make changes in serveradmin, only then alter the configuration files that come with the operating system by default. I also recommend keeping backups of all configuration files that are altered and a log of what was altered in each, in order to help piece the server back together should it become unconfigured miraculously when a softwareupdate -all is run next.

August 15th, 2012

Posted In: Mac OS X, Mac OS X Server

Tags: , , , , , , , , , , , , , , , , , , , , , , , , , , , ,

Once upon a time I had to learn to script in bash. I’m still learning, as with most people, but I’m feeling pretty comfortable. I often have people ask me what is the easiest way to learn scripting and I find myself telling people to use the history command. Much of what people need beyond simply looking at their bash history involves variable substitution, loops and regular expressions. Tackled separately this makes a palatable experience. So then what makes object-oriented or interpretive languages such as perl or python so much more difficult? Is it the lack of a bash history? Let’s try and exercise and see about that. Open a Terminal window and type the word python. python You’ll see a line about version numbers, etc and then you will then be placed into an interactive python environment, called the interpreter. It looks like this: >>> At the prompt, simply type foo(bar) You will then see an error that looks something like: “I am zeh interpretah, you vill do az I say!” Actually it looks a bit more like: Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'foo' is not defined I like to call that Python’s middle finger. That means you did something wrong. Ya, I told ya’ to do something wrong. Why would I start out that way? To show that it doesn’t matter. You should still have the interpreter looking at’ ya, so no big deal. If it helps the old ego pretend I did something wrong, which my wife can tell you is something that’s never ever ever happened before (or not). Now let’s actually do something: set a variable. Type some string followed by an equals sign and then the content you want the variable to have. Numbers and integers go without quotes: >>> charles = 0 strings are single quoted), as follows (respectively): >>> charles = 'n00b' Now that you’ve set a variable print the output by typing just the string that is the name of the variable: >>> charles Of course, we could have just typed print n00b to see what the interpreter thinks of me: >>> print n00b Note: Variables do not have to be defined and disappear automatically when their scope has also disappeared. Now let’s talk about functions. Python comes with a number of functions built in. These start with a word and have something (or nothing) in parenthesis, which are arguments for the function (if you’ve got them). A function that Python comes with is exit(). To exit Python with no arguments, just say exit(): >>> exit() Exit without the parenthesis doesn’t work. That’s because even if there are no arguments, exit is a function and functions need parenthesis. There are a lot of functions that can be used, although to keep the interpreter zippy they’re not all active at the same time. Functions can be imported in groups, where each group is in a file and each file is a module. To import a module, use the import command, followed by the module to be imported: import readline Now let’s look at Python’s help pages using the built-in help function. We’re going to use help to show us methods supported for the redline module we imported earlier: >>> help(readline) Or to see a specific function’s help within a module (assuming it’s been documented): help(readline.add_history) One easy function to use is len(), which calculates the length of a string. To see the length of that charles variable: >>> len(charles) I know what you’re thinkin’: 4 seems high (otherwise I might be doing something other than writing this, right?!?!). Moving on. Time to make our own function. Let’s make a basic loop function. To do so, at the interpreter still, type the def to define a function followed by the name and the variables you want to send to it. Below we’ll begin creating a function called bestrobot, creating a variable that will be used, which we’re just gonna’ call input. Notice that the declaration ends with a colon (:). From here on the whitespace is pretty important. All items that begin with a single tab will be a part of this function until the next double carriage return. Hit tab and then let’s do a simple if/then. In this case best will be boolean, simplifying the if then. Notice each of those have a colon at the end as well, with an additional indent: >>>def daneel(best): ...     if best: ...             print 'daneel is the best' ...     else: ...             print 'you should get daneel' ... If we fire this up: >>> daneel(True) Then we should see: daneel is the best Next, let’s build a array. This is a pretty straight forward task. Let’s define a list of directory services. To do so, we’ll set a variable name, then in brackets define each item in the list: >>> dirservers =  ['Open Directory', 'Active Directory', 'eDirectory'] That’s not too bad. Next up, let’s do a loop, a staple in any scripters toolbox likely to be whipped out in the first 2 minutes of any scripting exercise. This is much like the if, where the for is followed by a condition, then a colon and on the subsequent line there will be a task assigned to occur: >>> for dirservers in list: ...     print dirservers Now, because these are in brackets, we can edit the list. But if they were in parenthesis instead then they would be immutable meaning that they couldn’t be edited during runtime (way faster tho). Also, again, the above white space defines the end of the set of commands that loops. Personally, I find allowing the white space to manage where the if/for separates from the else and where the function ends to be pretty straight forward. Arrays are one thing, but I find counters to be a pretty standard staple of why to use for loops. To set a counter that iterates 10 times, you don’t have to do a bunch of crazy stuff, you can just use the range() function to set a loop to count up: >>> for num in range(10): print num Next, let’s look at writing a script into a file. All python scripts should Python scripts should end with a py file extension (*.py). Python scripts should also start with the following line, identifying the interpreter: #!/usr/bin/python The next part of the file is used to import modules. Earlier, we interactively loaded the readline module. This script is going to accept a positional parameter, made possible using the sys module: import sys Now we’re going to fire up a function: def helloem(): sys.argv[1] would be the first parameter that sys brings in, 2 would be the second, etc. 0 is the actual name of the script. Below will be a simple print statement followed by a little text and then our positional parameter: print 'Ello ', sys.argv[1] Then call the function at the end: helloem() Put it all together and viola, ur first little python proggie, which I’ll call #!/usr/bin/python import sys def helloem(): print 'Ello ', sys.argv[1] helloem() Keep in mind Python scripts will need to be executable by the user account that is trying to execute the script (as with any script). Also, keep in mind that upon reading this that your computer has now been programmed to giggle at you in my voice if you attempt to run a script without executable privileges. Also keep in mind that this setting cannot be undone, even with a secure unerase of the whole drive and reinstall. Since secure unerase leaves behind a little data though the laughing will cut in and out, like VoIP calls to tech support silos in strange foreign lands such as Singapore, Beijing and Minneapolis, where apparently it snows a lot. Where was I? Right, run your script, passing that first argument to the script: python Emerald

February 21st, 2011

Posted In: Mac OS X, Mass Deployment, Unix

Tags: , , , , , , ,

CrashPlan Pro Server is a pretty cool tool with a lot of great features that can be used to back up client computers. There are a lot of things that CrashPlan Pro is good at out of the box, but there are also a lot of other things that CrashPlan Pro wasn’t intended for that it could be good at, given a little additional flexibility. The REST API that CrashPlan Pro uses provides a little flexibility and as with most APIs I would expect it to provide even more as time goes on. I often hear people run away screaming when REST comes up, thinking they’re going to have to learn some pretty complex scripting. And while the scripting can be complex, it doesn’t necessarily have to be. You can find a lot of handy information about the options available in the REST API at The very first example command that CrashPlan gives is the following:
Now, to use this in a very simple script, let’s look at it with curl. You are going to need to authenticate, so we’re going to inject that into the URL in much the same was that we would with something like, let’s say, WebDAV, SSH or FTP. If the server name were foundation.lan, the user name was daneel and the password was seldonrulez then the curl command would actually look like so (you could use the -u operator to inject the authentication information, but as you’ll see later I’d like to make those a bit less complex):
curl http://daneel:seldonrulez@foundation.lan:4280/rest/users?status=Active
Note: The default port for the web administration in CrashPlan Pro is 4280. This is simply going to output a list of Active users on the server. The reason it’s going to output only Active users is that we asked it to (reading from left to right after the rest is shown in the URL) query users, using the status attribute and specifying only to show us users whose status matches as Active. We could just as easily have requested all users by using the following (which just removes ?status=Active):
curl http://daneel:seldonrulez@foundation.lan:4280/rest/users
Each user has a unique attribute in their id. These are assigned in an ascending order, so we could also query for the user with an ID of 3 by simply following the users with their unique ID:
curl http://daneel:seldonrulez@foundation.lan:4280/rest/users/3
We could also query for all users with a given attribute, such as orgId (note that these attributes are case sensitive unlike many other things that start with http). For example, to find users with an orgID of 3:
curl http://daneel:seldonrulez@foundation.lan:4280/rest/users?orgId=3
The API doesn’t just expose looking at users though. You can look at Organizations (aka orgs), targets (aka mountPoints), server statistics (aka serverStats) and Computers (aka computers). These can be discovered by running the following command:
curl -i http://daneel:seldonrulez@foundation.lan:4280/rest/
To then see each Organization:
curl http://daneel:seldonrulez@foundation.lan:4280/rest/orgs
And to see each Computer:
curl http://daneel:seldonrulez@foundation.lan:4280/rest/computers
You can also perform compound searches fairly easily. For example, let’s say that we wanted to see
curl http://daneel:seldonrulez@foundation.lan:4280/rest/computers?userId=3&status=Active
These basic incantations of curl are simply getting information, which programmatically could also be specified using a -X operator (or –request if you like to type a lot) to indicate the type of REQUEST we’re sending (continuing on with our Code42 Sci-fi inspired example):
curl -X GET -H ‘Content-type: application/json’ http://daneel:seldonrulez@foundation.lan:4280/rest/orgs
The important thing about being able to indicate the type of REQUEST is that we can do more than GET: we can also POST and PUT. We also used the -H operator to indicate the type of data, which we’re specifying as application/json (per the output of a curl -i command against the server’s REST API URL). POST is used to create objects in the database whereas PUT is used update objects in the database. This could result in:
curl -i -H ‘Content-Type: application/json’ -d ‘{“username”: “charlesedge”, “password”: “test”, “firstName”: “Charles”, “lastName”: “Edge”, “orgId”: “3”}’ http://daneel:seldonrulez@foundation.lan:4280/rest/users
Once you are able to write data, you will then be able to script mass events, such as create new users based on a dscl loop using groups, remove users at the end of a school year (PUT {“status”: “Deactivated”}), mass change orgIds based on other variables and basically fully integrate CrashPlan Pro into the middleware that your environment might already employ.
Perl, Python, Ruby and PHP come with a number of options specifically designed for working with REST, which makes more complicated scripting much easier (such as with php’s curl_setopt); however, these are mostly useful if you already know those languages and the point of this article was to stay in shell scripting land. This allows you knock out simple tasks quickly, even if the good people at Code 42 didn’t think to add the specific features to their software that you might have in mind. Once you start to get into scripting more complex events, look to the Python examples at the bottom of the API Architecture page to get ya’ kickstarted!

November 4th, 2010

Posted In: cloud, Mac OS X, Mac OS X Server, Mac Security, Mass Deployment, Ubuntu, Unix

Tags: , , , , , , , , , , ,

There are a lot of versions of the popular perl scripting language out there, and depending on what version you may have written a script with you might find that using a different version than the one that comes with an OS by default can have a drastic impact on a script. In Mac OS X you can change the default version of perl that the perl and a2p command will use. Before doing so you should check the version of perl being used by default, which can be done using the perl command, followed by the -v option:
perl -v
By default, the OS currently uses version 5.10.0. To change the version, you would use the defaults command to change the defaults domain. You will add a key called Version with a string of the version you would like to use. For example, to switch to 5.8.8:
defaults write Version -string 5.8.8
To change it back to 5.10.0:
defaults write Version -string 5.10.0
You can also set perl to run in 32 bit mode:
defaults write Prefer-32-Bit -boolean TRUE
To put it back:
defaults write Prefer-32-Bit -boolean FALSE
Python provides the same functionality:
defaults write Version -string 2.6
Or to run Python in 32-bit mode:
defaults write Prefer-32-Bit -boolean TRUE

June 8th, 2010

Posted In: Mac OS X

Tags: , , , , , ,

May 26th, 2007

Posted In: Unix

Tags: ,

May 23rd, 2007

Posted In: Ubuntu, Unix

Tags: ,

Next Page »