Skip to content

Commit

Permalink
Merge pull request #211 from envato/andrewjhumphrey-parameter-store-p…
Browse files Browse the repository at this point in the history
…arameter-resolver

parameter_store parameter resolver.
  • Loading branch information
andrewjhumphrey authored Feb 23, 2018
2 parents 6ff8015 + e1f8f29 commit f9b2d0c
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 0 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,21 @@ db_password:
secret: db_password
```
### Parameter Store
An alternative to the secrets store, uses the AWS SSM Parameter store to protect
secrets. Expects a parameter of either `String` or `SecureString` type to be present in the
same region as the stack. You can store the parameter using a command like this

`aws ssm put-parameter --region <region> --name <parameter name> --value <secret> --type (String|SecureString)`

When doing so make sure you don't accidentally store the secret in your `.bash_history` and
you will likely want to set the parameter to NoEcho in your template.

```yaml
db_password:
parameter_store: ssm_parameter_name
```

### Security Group

Looks up a security group by name and returns the ARN.
Expand Down
47 changes: 47 additions & 0 deletions features/apply_with_parameter_store_parameters.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
Feature: Apply command with parameter_store parameter

Background:
Given a file named "stack_master.yml" with:
"""
stacks:
us-east-2:
vpc:
template: vpc.rb
"""
And a directory named "parameters"
And a file named "parameters/vpc.yml" with:
"""
vpc_cidr:
parameter_store: "/cucumber-test-vpc-cidr"
"""
And a SSM parameter named "/cucumber-test-vpc-cidr" with value "10.0.0.0/16" in region "us-east-2"
And a directory named "templates"
And a file named "templates/vpc.rb" with:
"""
SparkleFormation.new(:vpc) do
parameters.vpc_cidr do
type 'String'
end
resources.vpc do
type 'AWS::EC2::VPC'
properties do
cidr_block ref!(:vpc_cidr)
end
end
end
"""

Scenario: Run apply and create a new stack
Given I stub the following stack events:
| stack_id | event_id | stack_name | logical_resource_id | resource_status | resource_type | timestamp |
| 1 | 1 | vpc | Vpc | CREATE_COMPLETE | AWS::EC2::VPC | 2020-10-29 00:00:00 |
| 1 | 1 | vpc | vpc | CREATE_COMPLETE | AWS::CloudFormation::Stack | 2020-10-29 00:00:00 |
When I run `stack_master apply us-east-2 vpc --trace`
And the output should contain all of these lines:
| +--- |
| +VpcCidr: 10.0.0.0/16 |
And the output should match /2020-10-29 00:00:00 (\+|\-)[0-9]{4} vpc AWS::CloudFormation::Stack CREATE_COMPLETE/
Then the exit status should be 0
14 changes: 14 additions & 0 deletions features/step_definitions/parameter_store_steps.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Given(/^(?:a|the) SSM parameter(?: named)? "([^"]*)" with value "([^"]*)" in region "([^"]*)"$/) do |parameter_name, parameter_value, parameter_region|
Aws.config[:ssm] = {
stub_responses: {
get_parameter: {
parameter: {
name: parameter_name,
value: parameter_value,
type: "SecureString",
version: 1
}
}
}
}
end
1 change: 1 addition & 0 deletions lib/stack_master.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ module ParameterResolvers
autoload :LatestAmiByTags, 'stack_master/parameter_resolvers/latest_ami_by_tags'
autoload :LatestAmi, 'stack_master/parameter_resolvers/latest_ami'
autoload :Env, 'stack_master/parameter_resolvers/env'
autoload :ParameterStore, 'stack_master/parameter_resolvers/parameter_store'
end

module AwsDriver
Expand Down
31 changes: 31 additions & 0 deletions lib/stack_master/parameter_resolvers/parameter_store.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module StackMaster
module ParameterResolvers
class ParameterStore < Resolver

ParameterNotFound = Class.new(StandardError)

def initialize(config, stack_definition)
@config = config
@stack_definition = stack_definition
end

def resolve(value)
begin
resp = ssm.get_parameter(
name: value,
with_decryption: true
)
rescue Aws::SSM::Errors::ParameterNotFound
raise ParameterNotFound, "Unable to find #{value} in Parameter Store"
end
resp.parameter.value
end

private

def ssm
@ssm ||= Aws::SSM::Client.new(region: @stack_definition.region)
end
end
end
end
50 changes: 50 additions & 0 deletions spec/stack_master/parameter_resolvers/parameter_store_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
RSpec.describe StackMaster::ParameterResolvers::ParameterStore do

describe '#resolve' do

let(:config) { double(base_dir: '/base') }
let(:stack_definition) { double(stack_name: 'mystack', region: 'us-east-1') }
subject(:resolver) { described_class.new(config, stack_definition) }
let(:parameter_name) { 'TEST' }
let(:parameter_value) { 'TEST' }
let(:unknown_parameter_name) { 'NOTEST' }
let(:unencryptable_parameter_name) { 'SECRETTEST' }


context 'the parameter is defined' do
before do
Aws.config[:ssm] = {
stub_responses: {
get_parameter: {
parameter: {
name: parameter_name,
value: parameter_value,
type: "SecureString",
version: 1
}
}
}
}
end

it 'should return the parameter value' do
expect(resolver.resolve(parameter_name)).to eq parameter_value
end
end

context 'the parameter is undefined' do
before do
Aws.config[:ssm] = {
stub_responses: {
get_parameter:
Aws::SSM::Errors::ParameterNotFound.new(unknown_parameter_name, "Parameter #{unknown_parameter_name} not found")
}
}
end
it 'should raise and error' do
expect { resolver.resolve(unknown_parameter_name) }
.to raise_error(StackMaster::ParameterResolvers::ParameterStore::ParameterNotFound, "Unable to find #{unknown_parameter_name} in Parameter Store")
end
end
end
end

0 comments on commit f9b2d0c

Please sign in to comment.