Cloud Developer Tips

Using the new awscli with Chef and OpsWorks

We used to go through so much trouble to manipulate Amazon Web Services resources from the command-line. There were separate AWS command-line tools for each service, and each needed to be downloaded and had unique configuration, and each had its own unique output format. Eric Hammond wrote in detail about this. With the new awscli tool by Mitch Garnaat (of python boto fame) and his team at AWS, we have a single tool that does it all, uniformly, and can be simply configured. Thanks, Amazon!

But what if you need to use the awscli from within a Chef recipe? How do you install and configure the awscli with Chef and OpsWorks? I had this very same question on some recent projects. Here’s how to do this easily.

The awscli cookbook

I created the awscli cookbook to install and configure the awscli command line tools. Add this cookbook to your deployment and include the awscli::default recipe in your run list, and watch it work.

This cookbook supports some common deployment scenarios:

  • Installing the awscli “plain vanilla”, during the Chef “execute” stage.
  • Installing the awscli during the Chef “compile” phase, so it can be called from within Chef.
  • Configuring the awscli’s AWS access credentials.
  • Configuring multiple configuration profiles.

It does not need to be run on an AWS instance – you can use this cookbook to install the awscli anywhere that you can run Chef.

See the cookbook’s README for more details.

Using awscli with IAM Roles

It’s not obvious from the awscli documentation, but when an instance has an associated IAM Role, the awscli will automatically read its credentials from the instance’s IAM metadata. This will often be the case when you are using OpsWorks to launch your instances. In these circumstances, you don’t need to configure any awscli access credentials. But, make sure you use IAM to grant the IAM Role permissions for the AWS API calls you’ll be making with the awscli.

Why use the awscli from within Chef?

Chef recipes are written in Ruby, and it’s easy to parse and manipulate JSON in Ruby. The awscli outputs its responses in JSON format – so it’s easy to parse those responses into your ruby code. The two make a very handy combination. For example, here is how to wait for a newly-created EBS volume to be available (so you can attach it to an instance without an error):

    require 'json'
    volume_id = "vol-12345678"
    region = "us-east-1"
    command = ""
    case node[:platform]
    when 'debian','ubuntu'
      command << "/usr/local/bin/"
    when 'redhat','centos','fedora','amazon','scientific'
      command << "/usr/bin/"
    command << "aws --region #{region} ec2 describe-volumes --volume-ids #{volume_id} --output json""Waiting for volume #{volume_id} to be available")
    loop do
      shell ="#{command} 2>&1")
      if !shell.exitstatus
        raise "#{command} failed:" + shell.stdout
      jdoc = JSON.parse(shell.stdout)
      vol_state = jdoc["Volumes"].first["State"]
      Chef::Log.debug("#{volume_id} is #{vol_state}")
      if vol_state=="available"
        Chef::Log.debug("Waiting 5 more seconds for volume #{volume_id} to be ready...")
        sleep 5

The last ten lines show how easy it is to access the awscli output within your ruby code (in your chef recipe).

8 replies on “Using the new awscli with Chef and OpsWorks”

Great article, Shlomo and I love the new cookbook. Very useful.

But I do have to take exception with one point. Saying that AWS CLI is written by me is flattering but untrue. I’m just one person on the team and it takes the whole team to deliver quality software. So here’s my shout out to the rest of the AWS CLI team!

Great article Shlomo!! I am going to use awscli cookbook in our infrastructure. Great explanation of how it works. I need to use it to download s3 bucket data onto my AWS instances.

Thanks for posting this. It’s been difficult navigation the AWS documentation for opsworks and this has been very helpful.

Am I correct in thinking that there’s a typo in the sample code? Where the vol_state variable in vol_state = jdoc["Volumes"].first["State"] should be just state?

Leave a Reply

Your email address will not be published.