Deploying a multi-node application to AWS using chef-provisioning

This post is for people who are getting started with chef-provisioning and want to use it to deploy to AWS. It will take you through creating a couple of machines and deploying a simple application to them. In a future post, I’ll extend this to cover setting up some networking and infrastructure (VPC, subnets, security groups), but this post will assume you are using the default VPC created by AWS.

If you’re just looking to try chef-provisioning and you use Vagrant, you may want start with my other post: Deploying a multi-node application to Vagrant using chef-provisioning.

For an overview of chef-provisioning ( (formerly known as chef-metal), take a look at this Chef-Provisioning: Infrastructure as Code blog post. Also, see the Chef provisioning docs for more details.

Getting setup with chef-provisioning

Chef-provisioning is included in the latest ChefDK (0.3.6 at time of writing). Make sure you have this version or later installed by typing:

chef --version

If not, you can download or upgrade it here.

Create a new Chef repository to explore chef-provisioning:

cd ~
chef generate repo chefprov

We are going to use chef-client in local mode to run our provisioning recipes, so we want to set up a .chef directory that will be used specifically for this repo.

cd ~/chefprov
mkdir .chef
cd .chef

In the .chef directory, create a knife.rb file containing the following:

log_level                :info
current_dir = File.dirname(__FILE__)
node_name                "provisioner"
client_key               "#{current_dir}/dummy.pem"
validation_client_name   "validator"

Our workstation is going to behave like a chef-client talking to the local-mode server on our workstation,  so it needs a node name and a key. The key can be any well-formed key as the local-mode server will not validate it. For example:

ssh-keygen -f dummy.pem

Check the setup is working by performing an empty chef-client run:

chef-client -z

This will perform a local mode chef-client run with no recipes, using the built-in chef-zero server running on port 8889. You should see output similar to:

Starting Chef Client, version 11.18.0
[2015-01-31T16:16:43-06:00] INFO: *** Chef 11.18.0 ***
[2015-01-31T16:16:43-06:00] INFO: Chef-client pid: 14113
[2015-01-31T16:16:44-06:00] INFO: Run List is []
[2015-01-31T16:16:44-06:00] INFO: Run List expands to []
[2015-01-31T16:16:44-06:00] INFO: Starting Chef Run for provisioner
[2015-01-31T16:16:44-06:00] INFO: Running start handlers
[2015-01-31T16:16:44-06:00] INFO: Start handlers complete.
[2015-01-31T16:16:44-06:00] INFO: HTTP Request Returned 404 Not Found : Object not found: /reports/nodes/provisioner/runs
[2015-01-31T16:16:44-06:00] WARN: Node provisioner has an empty run list.
Converging 0 resources
[2015-01-31T16:16:44-06:00] INFO: Chef Run complete in 0.032696323 seconds
Running handlers:
[2015-01-31T16:16:44-06:00] INFO: Running report handlers
Running handlers complete
[2015-01-31T16:16:44-06:00] INFO: Report handlers complete
Chef Client finished, 0/0 resources updated in 1.117898047 seconds

If you’re curious, take a look at the ‘nodes/provisioner.json’ file. This is where the local-mode server stores its node data. You can also run commands like:

knife node show provisioner -z

This command will query the local-mode server and show summary details that it has about your provisioner node (i.e. your workstation).

Preparing the AWS client

To use chef-provisioning, you need to have the AWS CLI client installed. Follow the AWS CLI setup instructions to download and install the client, and to obtain your access keys.

If you are using an existing AWS account, please take appropriate precautions to make sure that you are working in a ‘sandbox’ that minimizes the chance of bad things when you get a script wrong. For example, you might configure your AWS client default to use a region where you do not have existing resources. To do this, edit the ~/.aws/config file and make sure  your selected region is in the default stanza. The following example sets us-west-2 (Oregon) as the default region:

[default]
region = us-west-2

Also check that you have the right access keys are configured as default. I prefer to separate these out into the ~/.aws/credentials file, rather than put them in the config file:

[default]
aws_access_key_id = AMADEUPACCESSKEY
aws_secret_access_key = AMadeUPSecreTACcesSKEYXXYyyyZzzZ1234

To make sure the AWS client is working, run the following command:

aws ec2 describe-availability-zones

It should give you a list of availability zones in the region you are using.

If, like me, you are a little more paranoid, you may want to create an IAM user with limited access to resources. I won’t cover this in detail, but below is an example policy that may be useful as a basis to restrict access. Feel free to skip over this to the next section!

  "Version": "2012-10-17",
  "Statement": [
        {
            "Sid": "AllowDescribeAndBasicSetup",
            "Effect": "Allow",
            "Action": ["ec2:Describe*", 
                "ec2:ImportKeyPair", 
                "ec2:CreateTags", 
                "ec2:ModifyInstanceAttribute" ],
            "Resource": "*"
        },
        {
            "Sid": "AllowInstanceResourceActions",
            "Effect": "Allow",
            "Action": ["ec2:RunInstances"],
            "Resource": [
                "arn:aws:ec2:us-west-2:632055226646:instance/*",
                "arn:aws:ec2:us-west-2:632055226646:network-interface/*",
                "arn:aws:ec2:us-west-2:632055226646:subnet/*",
                "arn:aws:ec2:us-west-2:632055226646:key-pair/*",
                "arn:aws:ec2:us-west-2:632055226646:security-group/*",
                "arn:aws:ec2:us-west-2:632055226646:volume/*",
                "arn:aws:ec2:us-west-2::image/ami-*"]
        },
        {
            "Sid": "AllowOtherInstanceActions",
            "Effect": "Allow",
            "Action": [
                  "ec2:TerminateInstances",
                  "ec2:StopInstances",
                  "ec2:RebootInstances",
                  "ec2:StartInstances"],
            "Resource": "arn:aws:ec2:us-west-2:632055226646:instance/*"
        },
        {
            "Sid": "AllowToSeeWhatCantDo",
            "Effect": "Allow",
            "Action": [
                  "sts:DecodeAuthorizationMessage"],
            "Resource": "*"
        }
  ]
}

Lines 3-8 allow the user to perform most query operations on any region, import key pairs (see later section on SSH access), and create tags (which is something the chef-provisioning resources like ‘machine’ do by default).

Lines 9-21 only allow the user to create instances and associate them with resources in the us-west-2 region. Lines 22-31 allow the user to manage the instances after creation.

Lines 32-38 are optional but can be useful. If the access policy is too restrictive, you will get a ‘You are not authorized to perform this operation’ message. Sometimes this will include an encoded message which gives you information about what you were not authorized to do. With the above authorization, you can run:

aws sts decode-authorization-message --encoded-message xxxxxxxxxxxxxxxx

Where “xxxxxxxxxxxxxxxx” is the encoded message.

Preparing SSH access into AWS

In order to run chef-client on the instances that you are going to create in AWS, you need to enable SSH access to those instances. There are two main things you need to do:

  • Setup a key-pair
  • Enable SSH access from your IP address

Setup a key-pair

Use the EC2 console to create a keypair in the region you are using. Download the private key (‘test2_aws.pem’) and save it in ~/.ssh. Make sure its permissions are read-only:

chmod 400 ~/.ssh/test2_aws.pem

You will also need the public key. You can retrieve this from the private key by running:

ssh-keygen -y -f ~/.ssh/test2_aws.pem > test2_aws.pub

and giving it the name of the file.

If you are using an IAM user without a console logon, generate a keypair using ssh-keygen then import it using the AWS CLI:

aws ec2 import-key-pair --key-name test2_aws --public-key-material file://test2_aws.pub

The ‘file://’ method of loading the file ensures that the key is base64 encoded, which is required to upload a key via the CLI.

Enable SSH access from your IP address

By default, AWS does not enable SSH from external sources into its VPCs. You need to use the EC2 console to allow inbound SSH access from your IP address, by adding a rule to a security group.

This post assumes you can add this rule to the default security group for the default VPC in the region you are using. This will allow immediate access to the machines we will create with chef-provisioning.  If you can’t do this, the examples won’t work without some manual intervention – i.e. you will need to add the security group to the created instances before you can run recipes on them.

We also need to let chef-provisioning know about the keys. Add the following to your ./chef/knife.rb file:

knife[:ssh_user] = "ubuntu"
knife[:aws_ssh_key_id] = 'test2_aws'
private_keys     'test2_aws' => '/home/christine/.ssh/test2_aws.pem'
public_keys      'test2_aws' => '/home/christine/.ssh/test2_aws.pub'

Line 1 is the user name to use when SSH’ing to the instance. For the standard Ubuntu image, this should be ‘ubuntu’. Line 2 specifies which key name to use for AWS, and Lines 3 & 4 setup the locations of the private and public keys.

Enable external access to the application

Our test application requires TCP access on port 3001. Open this port by adding a Custom TCP rule to the security group for the default VPC, allowing access from any IP address (CIDR block ‘0.0.0.0/0’).

The inbound rules should now look something like this:
sgrules

Creating the AWS instances

Create basic machine provisioning recipe

Our first pass at the chef-provisioning recipes will just create the instances, with nothing on them.

We will create two recipes. The first will set up the AWS-specific details. The second will create the machines.

aws-setup.rb

require 'chef/provisioning/aws_driver'
with_driver 'aws'

  with_machine_options :bootstrap_options => {
  :key_name => 'test2_aws',
  :instance_type => 't1.micro',
  :associate_public_ip_address => true
}

Lines 4-8 specify what sort of instances we want to create.

Line 2 tells chef to use the ‘chef-provisioning-aws’ provider. This provider is one of two AWS providers distributed with ChefDK, and is an alternative to the more established chef-provisioning-fog driver. I am using it because of its support for a growing range of other AWS resources (VPCs, security groups, S3, and others). To use the fog driver, replace ‘aws’ with ‘fog:aws’. You may also need to make other changes, for example ‘:instance_type’ is ‘flavor_id’ in the fog driver.

In Line 7, we choose the smallest and cheapest type of instance to experiment with.

Line 8 associates a public IP address with the instance, so that chef can SSH to it.

We are using the default AMI, which is currently Ubuntu 14.04.

The full set of ‘:bootstrap_options’ corresponds to the options listed for the AWS create-instance method.

The second recipe specifies a simple topology with two machines in it:

topo.rb

require 'chef/provisioning'
machine 'db'
machine 'appserver'

This recipe will create and start the machines, and bootstrap the chef-client onto them.

UPDATE: chef-provisioning-aws 1.2.1 introduces new default AMIs. If the command above fails with:

AWS::EC2::Errors::InvalidParameterCombination: Non-Windows instances with a 
virtualization type of 'hvm' are currently not supported for this instance type.

then replace t1.micro with t2.micro in the above:

  with_machine_options :bootstrap_options => {
  :key_name => 'test2_aws',
  :instance_type => 't2.micro',
  :associate_public_ip_address => true
}

UPDATE: If the above command fails with:

 
         Unexpected Error:
         -----------------
         ChefZero::ServerNotFound: No socketless chef-zero server on given port 8889

then add the following to each machine resource:

machine 'db' do
  chef_server( :chef_server_url => 'http://localhost:8889') 
end
machine 'appserver' do
 chef_server( :chef_server_url => 'http://localhost:8889') 
end

or add the following in the setup recipe:

with_chef_server "http://localhost:8889"

This problem exists in chefDK 6.0 to 6.2.

Run the recipe

Before proceeding, be aware that you will be charged for the resources that these recipes create. Make sure you delete any instances after you are done. I will tell you how to do that using chef-provisioning, but I advise you to logon to the EC2 console and making sure you have no instances left running when you are done.

To run the recipes, enter:
chef-client -z aws_setup.rb topo.rb

For each of the two machines, you should see the chef-client run create a node, wait for the machine to become connectable (this may take a while), bootstrap the chef-client and perform an empty run.

If you go to the EC2 console, you should see both machines (named ‘db’ and ‘appserver’) are up and running.

Working around SSH issue

If you are trying this with ChefDK 0.3.6 on Ubuntu, you may encounter the following error:

         ================================================================================
         Chef encountered an error attempting to load the node data for "db"
         ================================================================================

         Unexpected Error:
         -----------------
         NoMethodError: undefined method `gsub' for nil:NilClass

This is a known issue with chef-provisioning providing a bad URL for the local-mode server. If you can upgrade to chefDK 0.4.0, this problem has been fixed (but be aware that chefDK 0.4 embeds Chef 12 and not Chef 11).

A workaround for chefDK 0.3.6 is to create the following Gemfile in your chefprov directory:

source 'https://rubygems.org'

gem 'chef-dk'
gem 'chef-provisioning'
gem 'chef-provisioning-aws'
gem 'net-ssh', '=2.9.1'

and then run chef-client using:

bundle exec chef-client -z aws_setup.rb topo.rb

This will run the chef-client using a previous version of ‘net-ssh’, which avoids the problem.

You will likely need to use ‘bundle exec’ in front of all of the chef-client runs described in this post.

Setup and deploy the Application

Get the application cookbooks

The basic application we will install can be found in the ‘test-repo’ for the ‘knife-topo’ plugin on Github.

First, download the latest release of  the knife-topo repository and unzip it.

Then we will use ‘berks vendor’ to assemble the cookbooks we need to deploy this application:

cd knife-topo-0.0.11/test-repo
berks vendor
cp -R berks-cookbooks/* ~/chefprov/cookbooks

Line 2 uses the Berksfile to assemble all of the necessary cookbooks into the ‘berks-cookbooks’ directory.

Line 3 copies them into our ‘chefprov’ repo, where the local-mode server will look for them when it runs the chef-provisioning recipes.

Extend machine provisioning to include runlists

Now change the topo.rb provisioning recipe as follows:

require 'chef/provisioning'

machine 'db' do
  run_list ['apt','testapp::db']
end

machine 'appserver' do
  run_list ['apt','testapp::appserver']
end

and rerun the chef-client:
chef-client -z aws_setup.rb topo.rb

This time, the chef-client running on the two instances will execute the specified recipes, installing nodejs on ‘appserver’ and mongodb on ‘db’.

Deploy the application

We will now create a third recipe to deploy the application. We could have included this as part of the ‘topo.rb’ recipe, but I chose to make it a separate recipe, so it can be run independently.

Here’s what the recipe looks like:

deploy.rb

require 'chef/provisioning'

machine 'appserver' do
 run_list ['testapp::deploy']
 attribute ['testapp', 'user'], 'ubuntu'
 attribute ['testapp', 'path'], '/var/opt'
 attribute ['testapp', 'db_location'], lazy { search(:node, "name:db").first['ipaddress'] }
end

ruby_block "print out public IP" do
 block do
 appservernode = search(:node, "name:appserver").first
 Chef::Log.info("Application can be accessed at http://#{appservernode['ec2']['public_ipv4']}:3001")
 end
end

Line 4 runs the recipe to deploy the application.

Lines 5 to 7 set attributes on the node that customize the test application. For example, Line 7 sets the attribute node[‘testapp’][‘db_location’] to the IP address of the database server, which it looks up using a search for node information stored in the local-mode Chef server (i.e. in the ‘chefprov/nodes’ directory).

In Line 5, ‘lazy’ is used so that the search occurs during the converge phase of the chef-run, not during the compile phase. This is important if the ‘topo.rb’ and ‘deploy.rb’ recipes are run in a single runlist, because the IP address of the database server will only be known after the db machine resource has actually been executed in the converge phase.

Lines 8-13 print out the URL for the application, which uses the public IP address of the application server. This is executed in a ‘ruby_block’ resource so that it occurs in the converge phase once the application server has been created and configured.

Run the chef-client:
chef-client -z aws_setup.rb deploy.rb

At the end of the run, you should see something like:

  * ruby_block[print out public IP] action run[2015-01-31T21:28:38-06:00] INFO: Processing ruby_block[print out public IP] action run (@recipe_files::/home/christine/chefprov/deploy.rb line 9)
[2015-01-31T21:28:38-06:00] INFO: Application can be accessed at https://54.67.82.204:3001
[2015-01-31T21:28:38-06:00] INFO: ruby_block[print out public IP] called

    - execute the ruby block print out public IP
[2015-01-31T21:28:38-06:00] INFO: Chef Run complete in 21.74813493 seconds

Running handlers:
[2015-01-31T21:28:38-06:00] INFO: Running report handlers
Running handlers complete
[2015-01-31T21:28:38-06:00] INFO: Report handlers complete
Chef Client finished, 2/2 resources updated in 23.594399737 seconds

Browse to the application URL, and you should see something like:

 Congratulations! You have installed a test application using the knife topo plugin.

 Here are some commands you can run to look at what the plugin did:

    knife node list
    knife node show dbserver01
    knife node show appserver01
    knife node show appserver01 -a normal
    knife data bag show topologies test1
    cat cookbooks/testsys_test1/attributes/softwareversion.rb

Go to the knife-topo plugin on Github

Ignore the example commands as we did not use the knife-topo plugin.

Destroy the machines

To destroy the machines, create a recipe:

destroy.rb

require 'chef/provisioning'
machine 'db' do
  :destroy
end

machine 'appserver' do
  :destroy
end

And run it:
chef-client -z destroy.rb

You should see messages like:

  * machine[appserver] action destroy[2015-02-01T09:20:43-06:00] INFO: Processing machine[appserver] action destroy (@recipe_files::/home/christine/chefprov/destroy.rb line 7)

    - Terminate appserver (i-93a8db50) in us-west-1 ...[2015-02-01T09:20:46-06:00] INFO: Processing chef_node[appserver] action delete (basic_chef_client::block line 26)

    - delete node appserver at http://localhost:8889[2015-02-01T09:20:46-06:00] INFO: Processing chef_client[appserver] action delete (basic_chef_client::block line 30)
[2015-02-01T09:20:46-06:00] INFO: chef_client[appserver] deleted client appserver at http://localhost:8889

    - delete client appserver at clients

For both ‘db’ and ‘appserver’. If the run succeeds but you do not see these messages, you may have specified the wrong machine name.

Until you are confident in your scripts, you may want to use the EC2 console to make sure you have terminated the instances (don’t forget to navigate to the right region). You may also want to remove the added rules from the VPC default security group.

64 thoughts on “Deploying a multi-node application to AWS using chef-provisioning

  1. Pingback: Chef Provisioning with Microsoft Azure – part 1 – Stuart Preston's blog

    • You can specify a specific subnet using machine_options bootstrap_options either globally, or on the specific machine resource.

      For example:
      with_machine_options :bootstrap_options => {
      :instance_type => ‘t1.micro’,
      :associate_public_ip_address => true,
      :subnet => “xxxxxxx”,
      :security_group_ids => [ “xxxxx” ]
      }

      or

      machine ‘myinstance’ do
      add_machine_options({
      :subnet => “xxxxxxx”,
      :security_group_ids => [ “xxxxxxxxx” ]
      })

      In the driver I was using for the original blog post, ‘xxxx’ would have to be a resource name defined in the recipe, so it only really worked when you were also creating the subnet. The chef provisioning gems distributed in ChefDK 0.5.0 RC5 should work with actual existing AWS IDs. Hope that helps.

      Like

  2. Thank you for this post, it’s been *very* helpful.

    I think that this part:

    “chef-client -z deploy.rb”

    Needs to be:

    “chef-client -z aws-setup.rb deploy.rb”

    At least it didn’t work for me otherwise.

    Like

    • I’ve not used IAM roles via provisioning enough yet to blog about it yet. From the chef provisioning gitter, it looks like you’re making progress on that. I look forward to reading your blog 🙂

      Like

  3. Where is the ssh-user ubuntu hard coded. I am using my AMI with the user centos, but chef-client is trying to connect to the EC2 instance using the user ubuntu. I have updated the the user as ‘centos’ in the knife.rb.

    Like

    • If you’ve already created the instance, chef may be storing the user name in the machine data. Try deleting the machine (e.g. run delete recipe, but also make sure after that it not in the nodes directory in the chef repo) and creating it again.

      Like

      • I have created a new instance with a difference :image_id => “ami-dc1c2b8e” in with machine option but it is still trying to connect with ubuntu. Could you please let me know how can I change the username

        Like

  4. This run is working fine for the first time and when I remove the instance from the server and re-initiate it again I am getting empty class error at machine ‘db’ in topo.rb

    Like

    • Are you deleting the instance using the destroy recipe, or deleting the node in Chef using knife? If the latter, chef provisioning keeps additional metadata about the machine, and that could cause errors. Check whether there is data left in the data_bags directory for the db machine. If you’re still having the problem, perhaps you could post or email me more details.

      Like

  5. I have deleted the data in the node and now it is working fine And I had another query I have create a key file on ubuntu and provided as key name it is working fine and Now I want to create a linux Instance I have :image_id => “ami-dc1c2b8e” in machine options and :ssh_username => “ec2-user”, in machine_options but it is still trying to connect using ubuntu and failing as it is trying to connect to it using ubuntu@public_ip and getting failed. Could you please suggest me how should I proceed.

    Like

    • I would expect :ssh_username in machine_options to work… possibly its a similar problem where old bootstrap information is stored in the node data – try destroying the machine, checking data bag item is gone, and recreating it. If this is the case, it may be worth raising an issue on github as I’d think the new ssh_username should be picked up from machine_options in this case.

      You can also update knife[:ssh_user] = “ec2-user” in the knife.rb file, if you want this to be your default. You may still need to destroy the instance and recreate it for it to pick it up.

      Like

  6. Can we pass arguments to the chef-client run in the form of json to override the default attributes in a recipe during run time, I have tried the following command “sudo chef-client -z checking.rb -j file.json” but it doesn’t work. So could you please specify me how to pass the attributes to aws.

    Like

    • What you’re doing should work. I double-checked using the following:
      test.json:

      {
        "myattr": "HI"
      }
      

      test.rb:

      Chef::Log.warn("GOT #{node['myattr']}")
      

      Run:

      chef-client -z test.rb -j test.json
      

      Which includes a WARN: GOT HI in the output.

      Like

  7. Hi Christine Draper,
    I would like upload files to amazon AWS S3 buckets from my local repository using chef-provisioning. So could you please provide me a sample code for it.

    Like

  8. I have tried the following code to create a machine and assign security groups to it. I have specified port No 22, 80 for both inbound and outbound allowance with cidr_block ‘172.31.0.0/24’ it is able to create the instance but it is unable to assign public IP and unable to connect to the instance. Could you please help me in sorting out this issue. Please find the below code for reference.

    machine ‘apache’ do
    add_machine_options :ssh_username => ‘ec2-user’,
    :bootstrap_options => {
    :instance_type => ‘t2.micro’,
    :image_id => “ami-12663b7a”,
    :subnet => “public-test”,
    :security_group_ids => “default_1”,
    :associate_public_ip_address => true,
    }
    recipe ‘java’
    end

    Like

    • Some pointers that might help… if I get a chance, I’ll do some experimentation and post an update.

      The chef-provisioning-aws driver was updated fairly recently to use V2 of the AWS Ruby API, rather than V1. The V2 API moves associate_public_ip_address from being a top-level attribute, to being on the eth0 network interface:
      AWS V2 API Run Instances Option Hash
      I believe there was a fix to chef-provisioning-aws so that it should still support the top level attribute, but you might need to update the gem. If that doesn’t work, you might want to open a bug; you might also try specifying an eth0 network interface. As a workaround, you might also change the VPC so that it auto-assigns public IP addresses to instances on creation.

      Like

    • Using chef-provisioning-aws 1.7.0 (the latest), the following works for me:

      machine 'test' do
       machine_options bootstrap_options: {
          key_name: "mykey",
          associate_public_ip_address: true,
          subnet: 'subnet-58afec2f'
        }
      end
      

      The backwards compatibility code creates a network interface when it calls the AWS API:

      [2015-12-21T19:17:15-06:00] INFO: [Aws::EC2::Client 200 0.839178 0 retries] run_instances(key_name:"mykey",max_count:1,min_count:1,instance_type:"t2.micro",image_id:"ami-34405e55",network_interfaces:[{device_index:0,associate_public_ip_address:true,delete_on_termination:true,subnet_id:"subnet-58afec2f"}])  
      

      Looking at your example, I think you need to change the security_group_ids into an array i.e. [ “default_1” ]. That might be the cause of your problem.

      Like

  9. Hello Christine Draper, I have implemented auto scaling in aws I have provided a machine image as

    machine_image ‘apache’ do
    machine_options :ssh_username => ‘ec2-user’,
    :bootstrap_options => {
    :instance_type => ‘t2.micro’,
    :image_id => “ami-12663b7a”,
    :subnet => “public-test”,
    :security_group_ids => “default_1”,
    :associate_public_ip_address => true,
    }
    recipe ‘java’
    recipe ‘apache’
    end

    aws_auto_scaling_group ‘auto-scaling-name’ do
    availability_zones [‘us-east-1b’]
    desired_capacity 2
    min_size 2
    max_size 4
    launch_configuration ‘config’
    end

    Here I have 5 concerns

    1) It is making use of default security group instead of default_1 but using default_1 as security group while creating instance before creating an image.
    2) I would like to register the chef-client with chef-server but when my instance got terminated its chef-client is unable to register and communicate with chef-server
    3) services on the auto-scaling node are not getting started automatically like apache.
    4) using ubuntu as user instead of ec2-user.
    5) I have tried to provide image to machine while is also facing problem with username ubuntu and getting disconnected after the period.
    So how should I configure this infrastructure so that my infrastructure will be as per my expectation.

    Like

    • Some thoughts…
      1) Try making security_group_ids an array.
      2) If you want to register the auto-scaling instances, you need to clean out the /etc/chef/client.pem and node_name in client.rb before taking the image; and then recreate the client.rb with a unique node name on startup in the cluster and run chef-client to bootstrap. I’ve done something like this using CloudFormation and Chef, but not yet with chef provisioning. One approach might be to use /etc/rc0.d script for the cleanup and use launch config user_data for the bootstrap (use the user_data to run a script that you build into the image).
      3) The services not starting would suggest a problem with the recipes. Check that they are creating the service properly on a normal ‘machine’… try stopping and starting them, look for errors in httpd log, etc.
      4/5) :ssh_username should set the ssh user, but it sounds like it is still picking up the default user. Does it work for normal ‘machine’ resources? Maybe you have hit a bug – hard to tell without seeing some of the output/error messages. You may want to post an issue on github with more specific info.

      Like

      • Hi Christine Draper, thanks for the response I have resolved the first one by specifying with_machine_options and assigned security_group_ids at that place and that handled the situation but I was unable to configure that with machine_image, I will have a look at the second concern and I was sure that my service is working fine in individual machine and fourtha and fifth concerns also got resolved with with_machine_options only. But now I am facing a problem with auto_scaling group in which it is using assigned security group of aws_launch_configuration but I am facing the problem with the subnet it is using the default subnet instead of my subnet specified in aws_launch_configuration or aws_vpc and failing to create an instance specifying that the subnet and security groups belong to different subnets.

        aws_launch_configuration ‘config’ do
        image “apache_new_auto”
        options security_groups: “default_2”,
        :subnet => “public-test_1”
        end

        Like

      • In the AWS API, it doesnt look like launch_configuration has a subnet option. Looks like you can list subnets in the vpc_zone_identifier option on the autoscaling group (or alternatively list availability_zones). Hope that works!

        Like

  10. I have tried availability_zones option but it has no impact the subnet is selecting the availability zone of default means suppose if i specify us-east-1b then it is selecting us-east-1b of default zone not the subnet that I have specified through vpc details in security group.

    aws_subnet ‘public-test_1’ do
    vpc ‘test-vpc’
    availability_zone ‘us-east-1b’
    map_public_ip_on_launch true
    cidr_block “10.0.0.0/24”
    end

    aws_auto_scaling_group ‘auto-scaling-name’ do
    availability_zones [‘us-east-1b’]
    #vpc_zone_identifier [“public-test_1”]
    desired_capacity 1
    min_size 1
    max_size 2
    launch_configuration ‘config’
    end

    I am getting undefined method `vpc_zone_identifier’ for Chef::Resource::AwsAutoScalingGroup error with vpc_zone_identifier.

    Like

  11. aws_auto_scaling_group ‘auto-scaling-name’ do
    availability_zones [‘us-east-1b’]
    options :vpc_zone_identifier => [“public-test_1”]
    desired_capacity 1
    min_size 1
    max_size 2
    launch_configuration ‘config’
    end

    The above code is not showing any error but it is still using default security greoup and subnet in specified zone.

    Like

    • I did some experimenting. Seems like aws_autoscaling_group is still using the V1 API, so it is expecting ‘subnets’ in options. This worked for me:

      aws_auto_scaling_group "appserver_group" do
        launch_configuration 'appserver_config'
        max_size 2
        min_size 1
        desired_capacity 1
        options subnets: [ "my-subnet" ] 
      end
      

      Like

  12. Do we have any option to get the end point name of the rds instance created, If so could you please specify me how can we get it and could anyone specify me an example for executing a command on rds using machine_execute. Thanks in advance.

    Like

      • but the problem with aws_object is that end point will create after some time the rds gets created and at that time of access if the end point doesn’t get created how can we get it with sample_rds.aws_object.end_point.

        Like

      • As long as you are using it as an attribute to another resource, you can use lazy { sample_rds.aws_object.end_point } similar to the example for referencing a vpc id. Or you can put it inside a ruby_block resource that evaluates after the rds resource… anything that delays evaluation to convergence time rather than compilation time.

        If this doesnt give you what you need, may be worth trying a question either to the Chef mailing list or in the chef-provisioning gitter room – easier to share more about your specific case that way, and more people who may be able to help!

        Like

      • I would like to save the value to a file I have tried with ruby but I am getting id not found error. Can I have a sample code for this.

        Like

      • Here’s an example of the two approaches using a security group:

        sg = aws_security_group 'test-sg' do
          vpc 'test-vpc'
        end
        
        file '/tmp/sgid' do
          content lazy { sg.aws_object.id }
        end
        
        ruby_block 'write file using ruby' do
          block do
            File.open('/tmp/sgid2', 'w') { |file| file.write(sg.aws_object.id) }
          end
        end
        

        Like

  13. I have tried rdsinstance.aws_object.end_point, rdsinstance.aws_object.endpoint, rdsinstance.aws_object.endpoint_address and I am getting undefined method .. and for other objects that takes boolean as input is not alllowing it to convert to boolean and returning something like I am trying to get the endpoint of rds. I am following http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/RDS/DBInstance.html#master_username-instance_method as a reference. Could you please share me a reference link where can I get the exact list for accessing the object items like id, endpoint, …

    Like

  14. I want to run the chef-client from a java program and it has started successfully and executing the few resources and getting hanged in the middle and failing to run other resources. I waited for a period of 1 hr but found no progress in running the recipes. Could you please specify me is there any constraints on time limit or some other restrictions in AWS provisioning with chef

    Like

    • I don’t have any experience of running chef-client from a Java program. If the chef-client run works outside of Java, I’d suspect that the Java security model prevents the chef-client from doing everything it needs to, probably related to ssh and port forwarding.

      Like

  15. I was unable to assign specific security group to RDS instance with additional_options db_security_groups [‘deault_2’] it is creating RDS without error and using default security group and tried some other like security db_security_group_name, security_group,and some other but none of them worked for me. Could you please let me know which option will assign specified security group.

    Like

  16. Hi Christine, I have created an image and assigned it to auto scaling and it is creating a key while building image but it is not assigning the key to the instances created from auto scaling group. It is not assigning even default key also and I am using the latest gem.

    Like

  17. I have assigned security group with the below snippet of code and I had a reference to the following link https://github.com/chef/chef-provisioning-aws/issues/368 and it helped me
    security=aws_security_group ‘security_group’ do
    vpc ‘vpc’
    …..
    end

    aws_rds_instance “rds-inst” do
    engine “mysql”
    publicly_accessible true
    …….
    additional_options(
    lazy do
    {
    vpc_security_group_ids: [security.aws_object.id]
    }
    end
    )
    end

    Like

  18. Running the below code with machine_options is not adding any storage to my Instance. I would like to add storage to my root directory can I specify the as below or do I need to make any changes.
    block_device_mapping: [{
    device_name: ‘/dev/xvdf’,
    ebs: {
    volume_size: 8
    }
    }]

    Like

  19. yes I have specified it as a bootstrap option and I have added the code during the first client run itself. If I specify it outside the block I am getting some error

    Like

  20. I am trying to create a new instance using the following code and getting Argument error during the first client run.

    machine ‘Sample’ do
    add_machine_options :ssh_username => ‘ec2-user’,
    :bootstrap_options => {
    :instance_type => ‘t2.micro’,
    :image_id => “ami-2051294a”,
    :associate_public_ip_address => true,
    block_device_mapping: [{
    device_name: ‘/dev/sda1’,
    ebs: {
    volume_size: 6, # 1 GB
    delete_on_termination: true
    }
    }]
    }

    had an error: ArgumentError: unexpected value at params[:block_device_mapping]

    could you please let me know what is the exact problem in the code

    Like

  21. Why we have error “HTTP Request Returned 404 Not Found: Object not found:” also when I wrote my .rb script then after running the script I had error 404 for chef-zero with many objects being not found?? like aws_vpc

    Like

    • 404 means chef-zero was looking for an existing object (e.g. the data bag item it uses to store information about previously created resources such as the aws_vpc) and did not find it. It isn’t always an error – did your actual client run fail? If it did, you’ll likely get more help posting on the Chef discourse https://discourse.chef.io/ as I am not actively working with Chef right now.

      Like

  22. I am getting “undefined method `id’ for nil:NilClass” error while executing the machine resource but the image is getting created. At the same time when I implemented action :destroy it is showing me as successful but I was still able to see the machine in running state.

    Following is my code

    machine ‘a’ do
    add_machine_options :ssh_username => ‘ubuntu’,
    :bootstrap_options => {
    :instance_type => ‘t2.micro’,
    :associate_public_ip_address => true,
    # :image_id => “ami-2d39803a”,
    :availability_zone => ‘us-east-1b’,
    }
    # action :destroy
    end

    It would be a great help to me If I can get some assistance.

    Like

  23. Hi, i have this problem, can you help me with this?

    chef-client -z aws_setup.rb topo.rb

    [2016-10-19T13:38:59-03:00] INFO: Auto-discovered chef repository at /home/nico/Documents/chefdk-provision-demo
    [2016-10-19T13:38:59-03:00] INFO: Started chef-zero at chefzero://localhost:8889 with repository at /home/nico/Documents/chefdk-provision-demo
    One version per cookbook

    [2016-10-19T13:38:59-03:00] INFO: Forking chef instance to converge…
    Starting Chef Client, version 12.10.24
    [2016-10-19T13:38:59-03:00] INFO: *** Chef 12.10.24 ***
    [2016-10-19T13:38:59-03:00] INFO: Platform: x86_64-linux
    [2016-10-19T13:38:59-03:00] INFO: Chef-client pid: 19106
    [2016-10-19T13:39:00-03:00] INFO: GET /organizations/chef/nodes/provisioner
    [2016-10-19T13:39:00-03:00] INFO: Run List is []
    [2016-10-19T13:39:00-03:00] INFO: Run List expands to []
    [2016-10-19T13:39:00-03:00] INFO: Starting Chef Run for provisioner
    [2016-10-19T13:39:00-03:00] INFO: Running start handlers
    [2016-10-19T13:39:00-03:00] INFO: Start handlers complete.
    [2016-10-19T13:39:00-03:00] INFO: POST /organizations/chef/reports/nodes/provisioner/runs
    — POST BODY —
    {“action”:”start”,”run_id”:”f6da6c18-b8dd-40ff-b568-fb849f3573eb”,”start_time”:”2016-10-19 13:39:00 -0300″}
    — END POST BODY —
    [2016-10-19T13:39:00-03:00] INFO: HTTP Request Returned 404 Not Found: Object not found:
    resolving cookbooks for run list: []
    [2016-10-19T13:39:00-03:00] INFO: POST /organizations/chef/environments/_default/cookbook_versions
    — POST BODY —
    {“run_list”:[]}
    — END POST BODY —
    [2016-10-19T13:39:00-03:00] INFO: Loading cookbooks []
    Synchronizing Cookbooks:
    Installing Cookbook Gems:
    Compiling Cookbooks…

    Running handlers:
    [2016-10-19T13:39:00-03:00] ERROR: Running exception handlers
    Running handlers complete
    [2016-10-19T13:39:00-03:00] ERROR: Exception handlers complete
    Chef Client failed. 0 resources updated in 01 seconds
    [2016-10-19T13:39:00-03:00] FATAL: Stacktrace dumped to /home/nico/Documents/chefdk-provision-demo/.chef/local-mode-cache/cache/chef-stacktrace.out
    [2016-10-19T13:39:00-03:00] FATAL: Please provide the contents of the stacktrace.out file if you file a bug report
    [2016-10-19T13:39:00-03:00] ERROR: could not find recipe file /home/nico/Documents/chefdk-provision-demo/aws_setup.rb
    [2016-10-19T13:39:01-03:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1)

    Like

    • The error its reporting is that the recipe file (aws_setup.rb) isn’t in the directory it’s expecting (/home/nico/Documents/chefdk-provision-demo/). Move them into that directory, or give full paths to them.

      If that doesn’t work, add the following into the chef/knife.rb in the repo directory:

      current_dir = File.dirname(__FILE__)
      cookbook_path ["#{current_dir}/../cookbooks"]
      

      And create an empty cookbooks directory in the repo. It looks like the chef-client is auto-discovering the repo location by looking for a cookbooks dir, which it does if there isn’t a cookbook_path set in the configuration file.

      Like

  24. Hi Christine,

    Your Instructions:
    You will also need the public key. You can retrieve this from the private key by running:

    ssh-keygen -y > test2_aws.pub

    and giving it the name of the file.

    Is this what you mean ?
    $ ssh-keygen -y -f id_rsa > test2_aws.pub

    Thanks,
    Mariano

    Like

Leave a comment