Skip to content

Commit f9b2d0c

Browse files
Merge pull request #211 from envato/andrewjhumphrey-parameter-store-parameter-resolver
parameter_store parameter resolver.
2 parents 6ff8015 + e1f8f29 commit f9b2d0c

6 files changed

Lines changed: 158 additions & 0 deletions

File tree

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,21 @@ db_password:
240240
secret: db_password
241241
```
242242
243+
### Parameter Store
244+
An alternative to the secrets store, uses the AWS SSM Parameter store to protect
245+
secrets. Expects a parameter of either `String` or `SecureString` type to be present in the
246+
same region as the stack. You can store the parameter using a command like this
247+
248+
`aws ssm put-parameter --region <region> --name <parameter name> --value <secret> --type (String|SecureString)`
249+
250+
When doing so make sure you don't accidentally store the secret in your `.bash_history` and
251+
you will likely want to set the parameter to NoEcho in your template.
252+
253+
```yaml
254+
db_password:
255+
parameter_store: ssm_parameter_name
256+
```
257+
243258
### Security Group
244259

245260
Looks up a security group by name and returns the ARN.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
Feature: Apply command with parameter_store parameter
2+
3+
Background:
4+
Given a file named "stack_master.yml" with:
5+
"""
6+
stacks:
7+
us-east-2:
8+
vpc:
9+
template: vpc.rb
10+
"""
11+
And a directory named "parameters"
12+
And a file named "parameters/vpc.yml" with:
13+
"""
14+
vpc_cidr:
15+
parameter_store: "/cucumber-test-vpc-cidr"
16+
"""
17+
And a SSM parameter named "/cucumber-test-vpc-cidr" with value "10.0.0.0/16" in region "us-east-2"
18+
And a directory named "templates"
19+
And a file named "templates/vpc.rb" with:
20+
"""
21+
SparkleFormation.new(:vpc) do
22+
23+
parameters.vpc_cidr do
24+
type 'String'
25+
end
26+
27+
resources.vpc do
28+
type 'AWS::EC2::VPC'
29+
properties do
30+
cidr_block ref!(:vpc_cidr)
31+
end
32+
end
33+
34+
end
35+
"""
36+
37+
Scenario: Run apply and create a new stack
38+
Given I stub the following stack events:
39+
| stack_id | event_id | stack_name | logical_resource_id | resource_status | resource_type | timestamp |
40+
| 1 | 1 | vpc | Vpc | CREATE_COMPLETE | AWS::EC2::VPC | 2020-10-29 00:00:00 |
41+
| 1 | 1 | vpc | vpc | CREATE_COMPLETE | AWS::CloudFormation::Stack | 2020-10-29 00:00:00 |
42+
When I run `stack_master apply us-east-2 vpc --trace`
43+
And the output should contain all of these lines:
44+
| +--- |
45+
| +VpcCidr: 10.0.0.0/16 |
46+
And the output should match /2020-10-29 00:00:00 (\+|\-)[0-9]{4} vpc AWS::CloudFormation::Stack CREATE_COMPLETE/
47+
Then the exit status should be 0
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Given(/^(?:a|the) SSM parameter(?: named)? "([^"]*)" with value "([^"]*)" in region "([^"]*)"$/) do |parameter_name, parameter_value, parameter_region|
2+
Aws.config[:ssm] = {
3+
stub_responses: {
4+
get_parameter: {
5+
parameter: {
6+
name: parameter_name,
7+
value: parameter_value,
8+
type: "SecureString",
9+
version: 1
10+
}
11+
}
12+
}
13+
}
14+
end

lib/stack_master.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ module ParameterResolvers
6161
autoload :LatestAmiByTags, 'stack_master/parameter_resolvers/latest_ami_by_tags'
6262
autoload :LatestAmi, 'stack_master/parameter_resolvers/latest_ami'
6363
autoload :Env, 'stack_master/parameter_resolvers/env'
64+
autoload :ParameterStore, 'stack_master/parameter_resolvers/parameter_store'
6465
end
6566

6667
module AwsDriver
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
module StackMaster
2+
module ParameterResolvers
3+
class ParameterStore < Resolver
4+
5+
ParameterNotFound = Class.new(StandardError)
6+
7+
def initialize(config, stack_definition)
8+
@config = config
9+
@stack_definition = stack_definition
10+
end
11+
12+
def resolve(value)
13+
begin
14+
resp = ssm.get_parameter(
15+
name: value,
16+
with_decryption: true
17+
)
18+
rescue Aws::SSM::Errors::ParameterNotFound
19+
raise ParameterNotFound, "Unable to find #{value} in Parameter Store"
20+
end
21+
resp.parameter.value
22+
end
23+
24+
private
25+
26+
def ssm
27+
@ssm ||= Aws::SSM::Client.new(region: @stack_definition.region)
28+
end
29+
end
30+
end
31+
end
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
RSpec.describe StackMaster::ParameterResolvers::ParameterStore do
2+
3+
describe '#resolve' do
4+
5+
let(:config) { double(base_dir: '/base') }
6+
let(:stack_definition) { double(stack_name: 'mystack', region: 'us-east-1') }
7+
subject(:resolver) { described_class.new(config, stack_definition) }
8+
let(:parameter_name) { 'TEST' }
9+
let(:parameter_value) { 'TEST' }
10+
let(:unknown_parameter_name) { 'NOTEST' }
11+
let(:unencryptable_parameter_name) { 'SECRETTEST' }
12+
13+
14+
context 'the parameter is defined' do
15+
before do
16+
Aws.config[:ssm] = {
17+
stub_responses: {
18+
get_parameter: {
19+
parameter: {
20+
name: parameter_name,
21+
value: parameter_value,
22+
type: "SecureString",
23+
version: 1
24+
}
25+
}
26+
}
27+
}
28+
end
29+
30+
it 'should return the parameter value' do
31+
expect(resolver.resolve(parameter_name)).to eq parameter_value
32+
end
33+
end
34+
35+
context 'the parameter is undefined' do
36+
before do
37+
Aws.config[:ssm] = {
38+
stub_responses: {
39+
get_parameter:
40+
Aws::SSM::Errors::ParameterNotFound.new(unknown_parameter_name, "Parameter #{unknown_parameter_name} not found")
41+
}
42+
}
43+
end
44+
it 'should raise and error' do
45+
expect { resolver.resolve(unknown_parameter_name) }
46+
.to raise_error(StackMaster::ParameterResolvers::ParameterStore::ParameterNotFound, "Unable to find #{unknown_parameter_name} in Parameter Store")
47+
end
48+
end
49+
end
50+
end

0 commit comments

Comments
 (0)