Multi-object CloudFront invalidation can be done inside the AWS Console already. But if you are like me who wishes to use the CLI, here's how its done.

  1. Download cfcurl.pl
    mkdir -p ~/aws/invalidation && cd !$
    wget http://d1nqj4pxyrfw2.cloudfront.net/cfcurl.pl
    chmod +x cfcurl.pl
    
  2. Setup credential file. Cfcurl by default will lookup .aws-secrets file at cfcurl.pl's current directory. But you can also put the same file at your home directory
    cat << 'EOF' > .aws-secrets
    %awsSecretAccessKeys = (
        # primary account
        'primary' => {
            id => 'Your Access Key ID', 
            key => 'Your Secret Access Key',
        },
    );
    EOF
    chmod 600 .aws-secrets
    
  3. Make the invalidation request file. This should be a valid XML
    cat << 'EOF' > files.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <InvalidationBatch>
    <CallerReference>201210162</CallerReference>
    <Path>/path/to/file1.jpg</Path>
    <Path>/path/to/file2.jpg</Path>
    <Path>/path/to/file3.jpg</Path>
    <Path>/path/to/file4.jpg</Path>
    </InvalidationBatch>
    EOF
    
    The example above assumes that your cloudfront url looks similar to http://d46bt2172abc1.cloudfront.net/path/to/file1.jpg

    Also note that the CallerReference should be unique value per request. The current unix timestamp should do the trick.

  4. Submit the request. Make sure that you substitute [distribution ID] with your own. You can get this information from the AWS Console
    ./cfcurl.pl --keyname primary -- -X POST -H "Content-Type: text/xml; charset=UTF-8" --upload-file files.xml https://cloudfront.amazonaws.com/2012-07-01/distribution/[distribution ID]/invalidation
    
    The XML output should indicate an "InProgress" status. Otherwise, check the credential file and the invalidation request file. Upon successful submission, the actual invalidation may take 5 to 15 minutes. You may check the status of each invalidation request by following the next step.

  5. Check invalidation status. Substitute [distribution ID] and [request ID]. You can get the [request ID] from the invalidation request output in the previous step ( tag)
    ./cfcurl.pl --keyname primary -- -X GET https://cloudfront.amazonaws.com/2012-07-01/distribution/[distribution ID]/invalidation/[request ID]
    


Invalidation Request Generator


If you have a local copy of the static files directory tree and if you want to invalidate a lot of files, you can use the following script to generate files.xml. Note that the current limit for invalidation is 3 concurrent requests and 1000 files per request.
#!/usr/bin/perl

use File::Basename;
use File::Find;
use POSIX qw(strftime);

$usage = <<"";
Usage:
$0 srcdir
srcdir is the associated mathjax source directory on this machine

$SRCDIR = shift or die $usage;
$CALLER_REF = strftime("%Y%m%d%H%M%S", localtime);

($fname, $abs_srcdir) = fileparse("$SRCDIR/");
chop $abs_srcdir;

sub handler {
my $path = shift;
$path =~ s/\ /%20/g;
print <<TOUT;
<Path>/${path}</Path>
TOUT
}

# Generate output
print <<TOUT;
<?xml version="1.0" encoding="UTF-8"?>
<InvalidationBatch>
<CallerReference>${CALLER_REF}</CallerReference>
TOUT

find(\&wanted, $abs_srcdir);

print <<TOUT;
</InvalidationBatch>
TOUT

sub wanted { # Reject non-files, and anything in .git or fonts dirs
 $_ = $File::Find::name;
 -f or return;
 s/^${abs_srcdir}\///;
 /^.git\// and return;
 /^fonts\// and return;
 /.svn\// and return;
 /^.DS_Store\// and return;
 /^rename.py\// and return;
 handler($_);
}
Download File
Usage:

./purge_request.pl /folder/to/static/files > files.xml