Copying AMIs with Tags and Launch Permissions
Among the many odd-in-hindsight decisions that can be found when working with the Amazon APIs on a daily basis, the one that most frequently springs to mind for me these days is that you cannot copy an AMI with its tags and launch permissions in a single operation. From an end user perspective, it would be very helpful to have a couple of additional flags added to the copy-image endpoint in order to specify that the destination image should have the same tags and launch permissions as the source. Sadly, that isn't the case. If you want those tags and launch permissions to exist for the destination image, then you have to add them yourself.
This is somewhat annoying, as I near always find myself in a situation in which I want a copied image to have the same tags and launch permissions as the original, at least at the outset. In any formal AWS architecture, tags are very important: for cost control, for identification of team ownership, for identification of function, and so forth. The usual scenario under which I might find myself copying an AMI is that I have packaged a release for a multi-region deployment into an AMI in one region, tagged it, and I now want to copy it to all of the regions in which it will be deployed. That deployment absolutely requires the same tags and launch permissions as the original. Other scenarios in which I might not care about tagging or desire different tags on the copy are somewhat less common, and in my case at least, usually one-off occasions that are not worth automating.
Scripting the copy of an AMI and then applying tags and launch permissions might sound like not so much work, but it is pretty annoying to have to redo over and again in every new devops codebase. It is just a few calls, but then one has to consider retries, error handling, and the fact that one has to wait an indeterminate length of time for the copy to complete before proceeding with whatever comes next:
- Load the AMI information from the describe-images endpoint to obtain name, description, and tags.
- Load the AMI launch permissions from the entirely different describe-image-attribute endpoint.
- Start the copy running via the copy-image endpoint. It will return the ID of the destination AMI.
- Every so often check to see that it is done via describe-images, or alternatively use image-available.
- Apply the tags to the destination AMI via the create-tags endpoint.
- Apply the launch permissions to the destination AMI via the modify-image-attribute endpoint.
It can become a modestly lengthy piece of code, even without all the frills. Certainly a day of work if setting it up from scratch, adding test cases, and so forth. After the second or third time writing the same functions into a different devops codebase and different language, I gave up and created the clone-ami-to-region NPM package; I'll be using that for this task going forward, wherever possible.
var cloneAmiToRegion = require('clone-ami-to-region'); cloneAmiToRegion.cloneImage({ // -------------------------------------------------------------- // Required configuration. // -------------------------------------------------------------- // Provide the source AMI ID. sourceImageId: 'ami-11223344', // The region that contains the source AMI. sourceRegion: 'us-east-1', // An array of regions to clone the AMI to. destinationRegions: [ 'eu-west-1', 'eu-west-2' ], // -------------------------------------------------------------- // Optional configuration. // -------------------------------------------------------------- // AWS credentials will be taken from the environment if not provided, and // that is the preferred methodology. Credentials and other configuration can // be provided directly, however. // clientOptions: { // accessKeyId: 'akid', // secretAccessKey: 'secret' // } // It usually takes a few minutes for a copy to complete. This determines the // frequency with which completion is checked. progressCheckIntervalInSeconds: 30, }, function (error, results) { if (error) { console.error(error); } // A results object is always returned, regardless of error status. It shows // the details of success or failure for each of the destinaton regions. It // has the form: // // { // 'eu-west-1': { // imageId: 'ami-11223344', // success: true // }, // 'us-west-2': { // error: new Error(''), // success: false // }, // ... // } // console.info(JSON.stringify(results, null, ' ')); });