Using the SDK for Ruby on a Vagrant Instance
Important
The AWS OpsWorks Stacks service reached end of life on May 26, 2024 and has been disabled for both new and existing customers.
We strongly recommend customers migrate their workloads to other solutions as soon as possible. If you have questions about migration, reach out to the AWS Support Team on AWS re:Post
This topic describes how a recipe running on a Vagrant instance can use the AWS SDK for Ruby to download a file from Amazon S3. Before starting, you must first have a set of AWS credentials—an access key and a secret access key—that allow the recipe to access Amazon S3.
Important
We strongly recommend that you do not use root account credentials for this purpose. Instead, create a user with an appropriate policy and provide those credentials to the recipe.
Be careful not to put credentials—even IAM user credentials—in a publicly accessible location, such as by uploading a file containing the credentials to a public GitHub or Bitbucket repository. Doing so exposes your credentials and could compromise your account's security.
Recipes running on an EC2Amazon EC2 instance can use an even better approach, an IAM role, as described in Using the SDK for Ruby on an AWS OpsWorks Stacks Linux Instance.
Content delivered to Amazon S3 buckets might contain customer content. For more information about removing sensitive data, see How Do I Empty an S3 Bucket? or How Do I Delete an S3 Bucket?.
If you don't already have an appropriate user, you can create one as follows. For more information, see What is IAM.
Warning
IAM users have long-term credentials, which presents a security risk. To help mitigate this risk, we recommend that you provide these users with only the permissions they require to perform the task and that you remove these users when they are no longer needed.
To create an IAM user
Sign in to the AWS Management Console and open the IAM console at https://console.aws.amazon.com/iam/
. -
In the navigation pane, choose Users and, if necessary, choose Add users to create a new administrative user.
-
On the Set permissions page, choose Attach policies directly.
-
Type
S3
in the Permissions policies search box to display the Amazon S3 policies.Choose AmazonS3ReadOnlyAccess. If you prefer, you can specify a policy that grants broader permissions, such as AmazonS3FullAccess, but standard practice is to grant only those permissions that are required. In this case, the recipe will only be downloading a file, so read-only access is sufficient.
-
Choose Next.
-
Choose Create user
-
Next create access keys for your user. For more information about creating access keys, see Managing access keys for IAM users in the IAM User Guide.
You must next provide a file to be downloaded. This example assumes that you will
put a file named myfile.txt
in a newly created S3 bucket named
cookbook_bucket
.
To provide a file for downloading
-
Create a file named
myfile.txt
with the following text and save it in a convenient location on your workstation.This is the file that you just downloaded from Amazon S3.
-
On the Amazon S3 console
, create a bucket named cookbook_bucket
in the Standard region and uploadmyfile.txt
to the bucket.
Set the cookbook up as follows.
To set up the cookbook
-
Create a directory within
opsworks_cookbooks
nameds3bucket
and navigate to it. -
Initialize and configure Test Kitchen, as described in Example 1: Installing Packages.
-
Replace the text in
.kitchen.yml
with the following.--- driver: name: vagrant provisioner: name: chef_solo environments_path: ./environments platforms: - name: ubuntu-14.04 suites: - name: s3bucket provisioner: solo_rb: environment: test run_list: - recipe[s3bucket::default] attributes:
-
Add two directories to
s3bucket
:recipes
andenvironments
. -
Create an environment file named
test.json
with the followingdefault_attributes
section, replacing theaccess_key
andsecret_key
values with the corresponding keys for your user. Save the file to the cookbook'senvironments
folder.{ "default_attributes" : { "cookbooks_101" : { "access_key": "
AKIAIOSFODNN7EXAMPLE
", "secret_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
" } }, "chef_type" : "environment", "json_class" : "Chef::Environment" }
You have a variety of ways to provide credentials to a recipe running on an instance. The key consideration is limiting the chances of accidentally exposing the keys and compromising your account security. For that reason, using explicit key values in your code is not recommended. The example instead puts the key values in the node object, which allows the recipe to reference them by using node syntax instead of exposing literal values. You must have root privileges to access the node object, which limits the possibility that the keys might be exposed. For more information, see Best Practices for Managing AWS Access Keys.
Note
Notice that the example uses nested attributes, with
cookbooks_101
as the first element. This practice limits the
chance of a name collision if there are other access_key
or
secret_key
attributes in the node object.
The following recipe downloads myfile.text
from the
cookbook_bucket
bucket.
gem_package "aws-sdk ~> 3" do action :install end ruby_block "download-object" do block do require 'aws-sdk' s3 = Aws::S3::Client.new( :access_key_id => "#{node['cookbooks_101']['access_key']}", :secret_access_key => "#{node['cookbooks_101']['secret_key']}") myfile = s3.bucket['cookbook_bucket'].objects['myfile.txt'] Dir.chdir("/tmp") File.open("myfile.txt", "w") do |f| f.write(myfile.read) f.close end end action :run end
The first part of the recipe installs the SDK for Ruby, which is a gem package. The
gem_package
Note
Your instance usually has two Ruby instances, which are typically different
versions. One is a dedicated instance that is used by the Chef client. The other
is used by applications and recipes running on the instance. It's important to
understand this distinction when installing gem packages, because there are two
resources for installing gems, gem_packagegem_package
. chef_gem
is only for gem packages
used by Chef client.
The remainder of the recipe is a ruby_blockrequire 'aws-sdk'
statement before it executes the
gem_package
resource. Because the SDK for Ruby hasn't been installed yet,
compilation will fail.
Code in a ruby_block
resource isn't compiled until that resource is
executed. In this example, the ruby_block
resource is executed after
the gem_package
resource has finished installing the SDK for Ruby, so the
code will run successfully.
The code in the ruby_block
works as follows.
-
Creates a new
Aws::S3
object, which provides the service interface.The access and secret keys are specified by referencing the values stored in the node object.
-
Calls the
S3
object'sbucket.objects
association, which returns anAws::S3::Object
object namedmyfile
that representsmyfile.txt
. -
Uses
Dir.chdir
to set the working directory to/tmp
. -
Opens a file named
myfile.txt
, writes the contents ofmyfile
to the file, and closes the file.
To run the recipe
-
Create a file named
default.rb
with the example recipe and save it to therecipes
directory. -
Run
kitchen converge
. -
Run
kitchen login
to log in to the instance, and then runls /tmp
. You should see themyfile.txt
, along with several Test Kitchen files and directories.vagrant@s3bucket-ubuntu-1204:~$ ls /tmp install.sh kitchen myfile.txt stderr
You can also run
cat /tmp/myfile.txt
to verify that the file's content is correct.
When you are finished, run kitchen destroy
to terminate the
instance.