Build Status Coverage Status Gem Version

aws-ssm-env

This tool sets parameters acquired from AWS EC2 Parameter Store as environment variables.

By default, the last hierarchy of the parameter name is set as the environment variable name.

For example, if the parameter name is /staging/secure/DB_PASSWORD, the parameter value is set to ENV['DB_PASSWORD'].
The naming of environment variables is optional and can be customized. (described later)

Installation

This gem has been tested with ruby version 2.7 to 3.3.

gem install aws-ssm-env

Rails

# Gemfile
gem 'aws-ssm-env', group: :aws
# config/application.rb
if defined?(AwsSsmEnv)
  AwsSsmEnv.load(path: "/myapp/#{ENV['RAILS_ENV']}", recursive: true)
end

Other ruby program

require 'aws-ssm-env'
AwsSsmEnv.load!(begins_with: "myapp.#{ENV['RACK_ENV']}.")

Quick Start

First of all, register the parameters in AWS EC2 Parameter Store.

# ex) register /myservice/staging/RDS_PASSWORD with SecureString
aws ssm --region ap-northeast-1 put-parameter \
  --name /myservice/staging/RDS_PASSWORD \
  --type SecureString --value <secret value>

Then, set authentication information of AWS.
For example, you can use environment variables as follows,

export AWS_ACCESS_KEY_ID=YOURACCESSKEYID
export AWS_SECRET_ACCESS_KEY=YOURSECRETKEY
bundle exec rails start

Or, you can pass ssm_client_args as the argument for AwsSsmEnv#load:

AwsSsmEnv.load(
  path: "/myservice/#{ENV['RAILS_ENV']}",
  ssm_client_args: {
    access_key_id: 'ACCESS_KEY_ID',
    secret_access_key: 'SECRET_ACCESS_KEY',
    region: 'ap-northeast-1',
  }
)

You can also use Aws.config.


if defined?(AwsSsmEnv)
  AWS.config({
    access_key_id: 'ACCESS_KEY_ID',
    secret_access_key: 'SECRET_ACCESS_KEY',
    region: 'ap-northeast-1',
  })
  AwsSsmEnv.load(path: "/myservice/#{ENV['RAILS_ENV']}")
end

For details, refer to the document of aws-sdk.

Develop

Unit test

bundle exec rspec
bundle exec rubocop

Integration test

export AWS_ACCESS_KEY_ID=xxxx
export AWS_SECRET_ACCESS_KEY=xxxx
export AWS_REGION=xxxx
bundle exec rspec --tag integration

IAM users who run tests need the following authorization policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": [
        "ssm:PutParameter",
        "ssm:DeleteParameters",
        "ssm:DescribeParameters",
        "ssm:GetParameters*"
      ],
      "Resource": "*"
    }
  ]
}

Usage

Here are descriptions of the options passed to AwsSsmEnv#load.

decryption: [Boolean]

Flag indicating whether to decrypt SecureString parameters.
If true is specified, the value of the acquired SecureString parameter is decrypted.
In case of false it is set as encrypted and environment variable value.
Since it is a gem for this, the default is true (decrypt).

overwrite: [Boolean]

Specify whether to overwrite an already set environment variable.
If true is specified, even if the environment variable is set, it overwrites it with the acquired parameter value.
If false is specified, do not overwrite already set environment variables.
The default is false (do not overwrite).
If you invoke AwsSsmEnv#load!, This flag will automatically be set to true.

client: [Aws::SSM::Client]

Specify an instance of Aws::SSM::Client.
An option to set it if there are already created instances.
If there are no instances created, use ssm_client_args instead.

ssm_client_args: [Hash]

Specify a hash to pass to the constructor of Aws::SSM::Client.
If not specified, Aws::SSM::Client#new is called with an empty argument.
It is unnecessary when using environment variable or authentication information by EC2 InstanceProfile.

fetch: [Symbol, AwsSsmEnv::Fetcher, Object]

Specify parameter fetch strategy.
Possible values are :path,:begins_with or an instance of a class that implements AwsSsmEnv::Fetcher, or an instance of a class with a each method.
If nothing is specified, it is treated as :path.
But when begins_with (which is described later) is specified, it will automatically be :begins_with.

:fetch => :path or default

When :path is specified, AwsSsmEnv::PathFetcher which fetches parameter hierarchy by path specification is used.
In this case, the path argument described below is required. Also, the recursive argument (described later) is used.
When acquiring parameters in this way, you need ssm:GetParametersByPath authority for the specified path.
An example of the IAM policy is shown below.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "ssm:GetParametersByPath",
      "Resource": "arn:aws:ssm:YOUR_REGION:YOUR_ACCOUNT_ID:parameter/YOUR_PATH"
    }
  ]
}

:fetch => :begins_with

If :begins_with is specified, AwsSsmEnv::BeginsWithFetcher is used to fetch parameters starting from the character string specified by the parameter name.
In this case, the begins_with argument described below is required.
When acquiring parameters in this way, you need the authority of ssm:DescribeParameters and ssm:GetParameters for the specified path.
An example of the IAM policy is shown below.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "ssm:DescribeParameters",
      "Resource": "arn:aws:ssm:YOUR_REGION:YOUR_ACCOUNT_ID:parameter"
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "ssm:GetParameters",
      "Resource": "arn:aws:ssm:YOUR_REGION:YOUR_ACCOUNT_ID:parameter/YOUR_PREFIX*"
    }
  ]
}

other

If you specify an instance of a class that implements AwsSsmEnv::Fetcher infetch, or an instance with a each method, that instance will be used as is.

naming: [Symbol, AwsSsmEnv::NamingStrategy, Object]

Specify the naming strategy for the environment variable name.
Possible values are :basename,:snakecase or an instance of a class that implements AwsSsmEnv::NamingStrategy, or an instance of a class with a parse_name method.
If nothing is specified, it is treated as :basename.

:naming => :basename or default

If naming is not specified or :basename is specified, AwsSsmEnv::BasenameNamingStrategy whose variable name is the last hierarchy of the parameter hierarchy is used.
In this case, for example, if the parameter name is /myapp/production/DB_PASSWORD, the parameter value is set to ENV['DB_PASSWORD'].

:naming => :snakecase

When :snakecase is specified, AwsSsmEnv::SnakeCaseNamingStrategy which uses the underscore delimiter of the parameter name's slash delimiter and converts it to uppercase letters as the environment variable name is used.
In this case, for example, if the parameter name is /myapp/production/DB_PASSWORD, the parameter value is set to ENV['MYAPP_PRODUCTION_DB_PASSWORD'].
You can specify first part of string to exclude with the removed_prefix argument described below. In addition, you can specify characters to be converted to underscores with the delimiter option described below.
In the following example, the parameter /myapp/production/db.password is set to ENV['DB_PASSWORD'].

AwsSsmEnv.load!(
  naming: :snakecase,
  removed_prefix: '/myapp/production',
  delimiter: /[\/.]/
)

other

If you specify an instance of a class that implements AwsSsmEnv::NamingStrategy infetch, or an instance with a parse_name method, that instance is used as is.

path: [String]

Required if nothing is specified for fetch or if :path is specified.
This option specifies the path hierarchy for acquiring parameters from Parameter Store.
In the example below, the parameter immediately under /myapp/web/production is acquired.

AwsSsmEnv.load(path: '/myapp/web/production')

recursive: [Boolean]

Used when no parameter is specified for fetch option or when :path is specified.
If true is specified, acquires all parameters below the specified path hierarchy.
If nothing is specified this parameter, it is treated as false(one level).
In the following example, all parameters below /myapp/web/production are acquired.

AwsSsmEnv.load(path: '/myapp/web/production', recursive: true)

begins_with: [String, Array]

Required if :begins_with is specified in fetch.
You can specify the prefix of the parameter name to be acquired by this option.
It is also possible to specify more than one in an array (OR condition).
In the example below, parameters with names starting with myapp.web.production are acquired.

AwsSsmEnv.load(path: 'myapp.web.production')

removed_prefix: [String]

Used when :snakecase is specified in naming.
By this option, you can specify the prefix of the parameter name to exclude from the environment variable name.
If :removed_ prefix is not specified, and :begins_with or :path is specified, that will be used.

delimiter: [String, Regexp]

Used when :snakecase is specified in naming.
By this option, you can specify a string or a regular expression to be converted to an underscore.
The default is a slash (/).

fetch_size: [Integer]

Specify the number of parameters to be acquired with one execution of AWS API.
If :path is specified, the maximum value is 10 and the default is 10.
If :begins_with is specified, the maximum value is 50 and the default is 50.
Usually this parameter is never specified.

Security

Because you must grant authority to acquire secret information, careful attention is required for security operation.

When the EC2 InstanceProfile is set, the parameters can be seen by any account on EC2, It is necessary to improve the security level by preparing an IAM User separately from the EC2 InstanceProfile.

If it is only the administrator that you can log in to EC2, it is not much different from having it in a file.

Since AWS Fargate makes it difficult to execute commands on containers, this risk is mitigated.

License

Apache License 2.0

Contributors