This repository has been archived by the owner on Aug 22, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from neillturner/credentials-file-dev
support aws credentials file
- Loading branch information
Showing
12 changed files
with
550 additions
and
149 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
*.gem | ||
# *.gem |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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: | ||
|
||
|
@@ -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. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
@@ -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", | ||
|
@@ -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) | ||
|
@@ -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 |
Oops, something went wrong.