Skip to content
This repository has been archived by the owner on Aug 22, 2023. It is now read-only.

Commit

Permalink
credentials file
Browse files Browse the repository at this point in the history
  • Loading branch information
neillturner committed Nov 30, 2015
1 parent bd882cf commit f7f2338
Show file tree
Hide file tree
Showing 12 changed files with 550 additions and 149 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
*.gem
# *.gem
93 changes: 65 additions & 28 deletions README.rdoc → README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
= Knife CFN

= DESCRIPTION:
Knife CFN
=========

This is a Knife plugin for AWS Cloud Formation. This plugin gives knife the ability to validate, create, describe, and delete stacks.

= INSTALLATION:
Installation
------------

Be sure you are running the latest version Chef. Versions earlier than 0.10.0 don't support plugins:

Expand All @@ -16,64 +16,101 @@ This plugin is distributed as a Ruby Gem. To install it, run:

Depending on your system's configuration, you may need to run this command with root privileges.

= CONFIGURATION:
Configuration
-------------

In order to communicate with the Amazon's CloudFormation API you will have to tell Knife about your AWS Access Key and Secret Access Key. The easiest way to accomplish this is to create some entries in your `knife.rb` file:

```ruby
knife[:aws_access_key_id] = "Your AWS Access Key ID"
knife[:aws_secret_access_key] = "Your AWS Secret Access Key"
```

If your `knife.rb` file will be checked into a SCM system (ie readable by others) you may want to read the values from environment variables:

```ruby
knife[:aws_access_key_id] = ENV['AWS_ACCESS_KEY_ID']
knife[:aws_secret_access_key] = ENV['AWS_SECRET_ACCESS_KEY']
# Optional if you're using Amazon's STS
knife[:aws_session_token] = ENV['AWS_SESSION_TOKEN']
```

You also have the option of passing your AWS API Key/Secret into the individual knife subcommands using the `-A` (or `--aws-access-key-id`) `-K` (or `--aws-secret-access-key`) command options

```bash
# provision a new stack
$ knife cfn create test -f test.stack
```

If you are working with Amazon's command line tools, there is a good chance
you already have a file with these keys somewhere in this format:

AWSAccessKeyId=Your AWS Access Key ID
AWSSecretKey=Your AWS Secret Access Key

In order to communicate with the Amazon's CloudFormation API you will have to tell Knife about your AWS Access Key and Secret Access Key. The easiest way to accomplish this is to create some entries in your <tt>knife.rb</tt> file:

knife[:aws_access_key_id] = "Your AWS Access Key ID"
knife[:aws_secret_access_key] = "Your AWS Secret Access Key"
The new config file format used by Amazon's command line tools is also supported:

If your knife.rb file will be checked into a SCM system (ie readable by others) you may want to read the values from environment variables:
[default]
aws_access_key_id = Your AWS Access Key ID
aws_secret_access_key = Your AWS Secret Access Key

knife[:aws_access_key_id] = "#{ENV['AWS_ACCESS_KEY_ID']}"
knife[:aws_secret_access_key] = "#{ENV['AWS_SECRET_ACCESS_KEY']}"
In this case, you can point the <tt>aws_credential_file</tt> option to
this file in your <tt>knife.rb</tt> file, like so:

You also have the option of passing your AWS API Key/Secret into the individual knife subcommands using the <tt>-A</tt> (or <tt>--aws-access-key-id</tt>) <tt>-K</tt> (or <tt>--aws-secret-access-key</tt>) command options
```ruby
knife[:aws_credential_file] = "/path/to/credentials/file/in/above/format"
```

# provision a new stack
knife cfn create test -f test.stack
If you have multiple profiles in your credentials file you can define which
profile to use. The `default` profile will be used if not supplied,

```ruby
knife[:aws_profile] = "personal"
```

= SUBCOMMANDS:
Subcommands
-----------

This plugin provides the following Knife subcommands. Specific command options can be found by invoking the subcommand with a <tt>--help</tt> flag

== knife cfn validate
#### `knife cfn validate`

Validates a template file of template URL.
Validates a template file of template URL.

== knife cfn create
#### `knife cfn create`

Create a cloud formation stack from a template file or Template URL.
Create a cloud formation stack from a template file or Template URL.

== knife cfn update
#### `knife cfn update`

Update a cloud formation stack from a template file or Template URL.
Update a cloud formation stack from a template file or Template URL.

== knife cfn delete
#### `knife cfn delete`

Deletes a running cloud formation stack.
Deletes a running cloud formation stack.

== knife cfn describe [-l / --long ] [stack name]
#### `knife cfn describe [-l / --long ] [stack name]`

Outputs the name, status, creation time and rollback status of a stack, or all stacks if <tt>stack name</tt> is omitted.
The <tt>--long</tt> (<tt>-l</tt>) parameter shows stack IDs (ARN) instead of friendly names.

== knife cfn events [ stack name ]
#### `knife cfn events [ stack name ]`

Outputs a list of events for a stack name.

== knife cfn resources [ stack name ] [ logical resource id ]
#### `knife cfn resources [ stack name ] [ logical resource id ]`

Outputs the logical resource ID, physical resource ID, resource type and status for all resources of a stack. If <tt>logical resource
id</tt> is specified, then only the details of that resource is shown. A logical resource ID is reference given to a resource in the cloudformation
template, under the "Resources" section.

== knife cfn outputs [ -o ] [ stack name ]
#### `knife cfn outputs [ -o ] [ stack name ]`

Outputs a list of outputs for a stack name. If <tt>-o</tt> option is specified, then output will be formatted in the same syntax as parameters for cfn create / update

= LICENSE:
License
-------

Author:: Neill Turner ([email protected])
Copyright:: Copyright (c) 2012 EC2Dream.
Expand Down
4 changes: 2 additions & 2 deletions knife-cfn.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Gem::Specification.new do |s|
s.name = "knife-cfn"
s.version = "0.1.10"
s.version = "0.1.11"
s.summary = "CloudFormation Support for Knife"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.author = "Neill Turner"
Expand All @@ -11,6 +11,6 @@ Gem::Specification.new do |s|
s.homepage = 'https://github.com/neillturner/knife-cfn'
s.files = Dir["lib/**/*"]
s.rubygems_version = "1.6.2"
s.add_dependency "fog", ">= 1.3"
s.add_dependency 'fog', '~> 1.18'
s.add_dependency "chef", ">= 11"
end
117 changes: 101 additions & 16 deletions lib/chef/knife/cfn_base.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
#
# Author:: Seth Chisamore (<[email protected]>)
# Copyright:: Copyright (c) 2011-2015 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
Expand Down Expand Up @@ -30,6 +34,17 @@ def self.included(includer)
require 'chef/json_compat'
end

option :aws_credential_file,
:long => "--aws-credential-file FILE",
:description => "File containing AWS credentials as used by aws cmdline tools",
:proc => Proc.new { |key| Chef::Config[:knife][:aws_credential_file] = key }

option :aws_profile,
:long => "--aws-profile PROFILE",
:description => "AWS profile, from credential file, to use",
:default => 'default',
:proc => Proc.new { |key| Chef::Config[:knife][:aws_profile] = key }

option :aws_access_key_id,
:short => "-A ID",
:long => "--aws-access-key-id KEY",
Expand All @@ -42,27 +57,45 @@ def self.included(includer)
:description => "Your AWS API Secret Access Key",
:proc => Proc.new { |key| Chef::Config[:knife][:aws_secret_access_key] = key }

option :aws_session_token,
:long => "--aws-session-token TOKEN",
:description => "Your AWS Session Token, for use with AWS STS Federation or Session Tokens",
:proc => Proc.new { |key| Chef::Config[:knife][:aws_session_token] = key }

option :region,
:long => "--region REGION",
:description => "Your AWS region",
:default => "us-east-1",
:proc => Proc.new { |key| Chef::Config[:knife][:region] = key }

option :use_iam_profile,
:long => "--use-iam-profile",
:description => "Use IAM profile assigned to current machine",
:boolean => true,
:default => false,
:proc => Proc.new { |key| Chef::Config[:knife][:use_iam_profile] = key }
end
end

def connection
connection_settings = {
# :provider => 'AWS',
:region => locate_config_value(:region)
}
if locate_config_value(:use_iam_profile)
connection_settings[:use_iam_profile] = true
else
connection_settings[:aws_access_key_id] = locate_config_value(:aws_access_key_id)
connection_settings[:aws_secret_access_key] = locate_config_value(:aws_secret_access_key)
connection_settings[:aws_session_token] = locate_config_value(:aws_session_token)
end
@connection ||= begin
connection = Fog::AWS::CloudFormation.new(
:aws_access_key_id => Chef::Config[:knife][:aws_access_key_id],
:aws_secret_access_key => Chef::Config[:knife][:aws_secret_access_key],
:region => locate_config_value(:region)
)
connection = Fog::AWS::CloudFormation.new(connection_settings)
end
end

def locate_config_value(key)
key = key.to_sym
Chef::Config[:knife][key] || config[key]
config[key] || Chef::Config[:knife][key]
end

def msg_pair(label, value, color=:cyan)
Expand All @@ -71,23 +104,75 @@ def msg_pair(label, value, color=:cyan)
end
end

def is_image_windows?
image_info = connection.images.get(@server.image_id)
return image_info.platform == 'windows'
end

def validate!(keys=[:aws_access_key_id, :aws_secret_access_key])
errors = []

keys.each do |k|
pretty_key = k.to_s.gsub(/_/, ' ').gsub(/\w+/){ |w| (w =~ /(ssh)|(aws)/i) ? w.upcase : w.capitalize }
if Chef::Config[:knife][k].nil?
errors << "You did not provide a valid '#{pretty_key}' value."
unless locate_config_value(:use_iam_profile)
unless Chef::Config[:knife][:aws_credential_file].nil?
unless (Chef::Config[:knife].keys & [:aws_access_key_id, :aws_secret_access_key]).empty?
errors << "Either provide a credentials file or the access key and secret keys but not both."
end
# File format:
# AWSAccessKeyId=somethingsomethingdarkside
# AWSSecretKey=somethingsomethingcomplete
# OR
# [default]
# aws_access_key_id = somethingsomethingdarkside
# aws_secret_access_key = somethingsomethingdarkside

aws_creds = ini_parse(File.read(Chef::Config[:knife][:aws_credential_file]))
profile = Chef::Config[:knife][:aws_profile] || 'default'
entries = aws_creds.values.first.has_key?("AWSAccessKeyId") ? aws_creds.values.first : aws_creds[profile]

Chef::Config[:knife][:aws_access_key_id] = entries['AWSAccessKeyId'] || entries['aws_access_key_id']
Chef::Config[:knife][:aws_secret_access_key] = entries['AWSSecretKey'] || entries['aws_secret_access_key']
end

keys.each do |k|
pretty_key = k.to_s.gsub(/_/, ' ').gsub(/\w+/){ |w| (w =~ /(ssh)|(aws)/i) ? w.upcase : w.capitalize }
if Chef::Config[:knife][k].nil?
errors << "You did not provide a valid '#{pretty_key}' value."
end
end
end

if errors.each{|e| ui.error(e)}.any?
exit 1
if errors.each{|e| ui.error(e)}.any?
exit 1
end
end
end

end
end
end

def iam_name_from_profile(profile)
# The IAM profile object only contains the name as part of the arn
if profile && profile.key?('arn')
name = profile['arn'].split('/')[-1]
end
name ||= ''
end

def ini_parse(file)
current_section = {}
map = {}
file.each_line do |line|
line = line.split(/^|\s;/).first # remove comments
section = line.match(/^\s*\[([^\[\]]+)\]\s*$/) unless line.nil?
if section
current_section = section[1]
elsif current_section
item = line.match(/^\s*(.+?)\s*=\s*(.+?)\s*$/) unless line.nil?
if item
map[current_section] ||= {}
map[current_section][item[1]] = item[2]
end
end
end
map
end
end
end
Loading

0 comments on commit f7f2338

Please sign in to comment.