terraspace_plugin_aws 0.2.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe3bb9a8a7744b46c03ecb1abb577b0c0efd86e5304041b5e0c3e2aa085edb36
4
- data.tar.gz: 0c5c7d985be2fc6eb218ade8b698f4e4534038428c238af911f8bdab903be846
3
+ metadata.gz: eb8991c0ca2191cc5ec5d81a91afb2e1db4f7a7bdc8b1542d7e662ee55be2e05
4
+ data.tar.gz: f7bf38b1edc49e949643a9df6e70b0826ca5336c18e790513e094054a39ef09c
5
5
  SHA512:
6
- metadata.gz: 9aa446bf85feb83a954620b52222f3e28215a36e8769fdc4c620af2d02913ad7c81b4985674220d222c5ee95cf6226ce24cf8761d4bd991a6cb967037be11b3c
7
- data.tar.gz: a7fd615550e52f1ec56d1b1701f843c4fa9b21f664efd286e01552a1bc9b875db2ea1b02247b7961ab4788107551ad6247f7e330e09d2097680f33660a3b958f
6
+ metadata.gz: 4850daa5645efdde37d39ee36fc826bd82993c084d0deabafe83cbd7f182ce630e01d9b5cd7bcde45c366d1e9beeb766cd3f97f411723f9d843a861c4e0a62e5
7
+ data.tar.gz: e0229d052f5fb491562683391cec533cddb47cc453d08795200101f20e2b408f51241e1ebe73b4cd5622a5640df06d6236727e4345ae55512e98555f8f95305e
data/CHANGELOG.md CHANGED
@@ -3,6 +3,20 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  This project *loosely tries* to adhere to [Semantic Versioning](http://semver.org/).
5
5
 
6
+ ## [0.3.2] - 2021-12-14
7
+ - [#9](https://github.com/boltops-tools/terraspace_plugin_aws/pull/9) support separate aws account for s3 backend bucket
8
+
9
+ ## [0.3.1] - 2021-12-14
10
+ - [#8](https://github.com/boltops-tools/terraspace_plugin_aws/pull/8) use region configured in the backend.tf for the s3 client
11
+
12
+ ## [0.3.0] - 2020-11-15
13
+ - [#5](https://github.com/boltops-tools/terraspace_plugin_aws/pull/5) helper and secrets support
14
+ - aws_secret and aws_ssm helpers
15
+
16
+ ## [0.2.2]
17
+ - #4 default access logging to false
18
+ - set prefix to @folder for performance improvement
19
+
6
20
  ## [0.2.1]
7
21
  - #3 quiet s3-secure messages
8
22
 
data/README.md CHANGED
@@ -24,7 +24,7 @@ TerraspacePluginAws.configure do |config|
24
24
  config.s3.enforce_ssl = true
25
25
  config.s3.versioning = true
26
26
  config.s3.lifecycle = true
27
- config.s3.access_logging = true
27
+ config.s3.access_logging = false # false by default
28
28
  config.s3.secure_existing = false # run the security controls on existing buckets. by default, only run on newly created bucket the first time
29
29
 
30
30
  config.dynamodb.encryption = true
@@ -1,7 +1,7 @@
1
1
  terraform {
2
2
  backend "s3" {
3
- bucket = "<%%= expansion('terraform-state-:ACCOUNT-:REGION-:ENV') %>" # expanded by terraspace IE: terraform-state-112233445566-us-west-2-dev
4
- key = "<%%= expansion(':REGION/:ENV/:BUILD_DIR/terraform.tfstate') %>" # expanded by terraspace IE: us-west-2/dev/modules/vm/terraform.tfstate
3
+ bucket = "<%%= expansion('terraform-state-:ACCOUNT-:REGION-:ENV') %>"
4
+ key = "<%%= expansion(':REGION/:ENV/:BUILD_DIR/terraform.tfstate') %>"
5
5
  region = "<%%= expansion(':REGION') %>"
6
6
  encrypt = true
7
7
  dynamodb_table = "terraform_locks"
@@ -4,7 +4,7 @@ module TerraspacePluginAws
4
4
  class Autoloader
5
5
  class Inflector < Zeitwerk::Inflector
6
6
  def camelize(basename, _abspath)
7
- map = { cli: "CLI", version: "VERSION" }
7
+ map = { cli: "CLI", ssm: "SSM", version: "VERSION" }
8
8
  map[basename.to_sym] || super
9
9
  end
10
10
  end
@@ -0,0 +1,100 @@
1
+ module TerraspacePluginAws::Clients
2
+ module Options
3
+ private
4
+ def client_options
5
+ if @info['role_arn']
6
+ client_assume_role_config
7
+ else
8
+ client_default_options
9
+ end
10
+ end
11
+
12
+ # Typically, aws sdk client options are inferred from the user environment unless set in the backend.tf
13
+ #
14
+ # terraform s3 backend assume role configuration: https://www.terraform.io/docs/language/settings/backends/s3.html
15
+ #
16
+ # assume_role_duration_seconds - (Optional) Number of seconds to restrict the assume role session duration.
17
+ # assume_role_policy - (Optional) IAM Policy JSON describing further restricting permissions for the IAM Role being assumed.
18
+ # assume_role_policy_arns - (Optional) Set of Amazon Resource Names (ARNs) of IAM Policies describing further restricting permissions for the IAM Role being assumed.
19
+ # assume_role_tags - (Optional) Map of assume role session tags.
20
+ # assume_role_transitive_tag_keys - (Optional) Set of assume role session tag keys to pass to any subsequent sessions.
21
+ # external_id - (Optional) External identifier to use when assuming the role.
22
+ # role_arn - (Optional) Amazon Resource Name (ARN) of the IAM Role to assume.
23
+ # session_name - (Optional) Session name to use when assuming the role.
24
+ #
25
+ # ruby sdk: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/AssumeRoleCredentials.html
26
+ #
27
+ # :role_arn (required, String)
28
+ # :role_session_name (required, String)
29
+ # :policy (String)
30
+ # :duration_seconds (Integer)
31
+ # :external_id (String)
32
+ # :client (STS::Client)
33
+ #
34
+ def client_assume_role_config
35
+ whitelist = %w[
36
+ assume_role_duration_seconds
37
+ assume_role_policy
38
+ session_name
39
+ external_id
40
+ role_arn
41
+ ]
42
+ assume_role_config = @info.slice(*whitelist)
43
+ # not supported?
44
+ # assume_role_policy_arns
45
+ # assume_role_tags
46
+ # assume_role_transitive_tag_keys
47
+ # already matches
48
+ # external_id
49
+ # role_arn
50
+ # rest needs to be mapped
51
+ map = {
52
+ 'assume_role_duration_seconds' => 'duration_seconds',
53
+ 'assume_role_policy' => 'policy',
54
+ 'session_name' => 'role_session_name',
55
+ }
56
+ map.each do |terraform_key, ruby_sdk_key|
57
+ v = assume_role_config.delete(terraform_key)
58
+ assume_role_config[ruby_sdk_key] = v if v
59
+ end
60
+ assume_role_config.symbolize_keys! # ruby sdk expects symbols for keys
61
+ assume_role_config[:role_session_name] ||= [ENV['C9_USER'] || ENV['USER'], 'session'].compact.join('-') # session name is required for the ruby sdk
62
+ # options = {client: Aws::STS::Client.new(client_region_option)}
63
+ options = {}
64
+ options.merge!(assume_role_config)
65
+ role_credentials = Aws::AssumeRoleCredentials.new(options)
66
+ {credentials: role_credentials}
67
+ end
68
+
69
+ # terraform s3 backend configuration: https://www.terraform.io/docs/language/settings/backends/s3.html
70
+ #
71
+ # access_key - (Optional) AWS access key. If configured, must also configure secret_key. This can also be sourced from the AWS_ACCESS_KEY_ID environment variable, AWS shared credentials file (e.g. ~/.aws/credentials), or AWS shared configuration file (e.g. ~/.aws/config).
72
+ # secret_key - (Optional) AWS access key. If configured, must also configure access_key. This can also be sourced from the AWS_SECRET_ACCESS_KEY environment variable, AWS shared credentials file (e.g. ~/.aws/credentials), or AWS shared configuration file (e.g. ~/.aws/config).
73
+ #
74
+ # ruby sdk: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/Credentials.html
75
+ #
76
+ # access_key_id (String)
77
+ # secret_access_key (String)
78
+ # session_token (String) (defaults to: nil) — (nil)
79
+ #
80
+ def client_default_options
81
+ whitelist = %w[
82
+ access_key_id
83
+ secret_access_key
84
+ session_token
85
+ profile
86
+ ]
87
+ options = @info.slice(*whitelist)
88
+ options.symbolize_keys! # ruby sdk expects symbols for keys
89
+ client_region_option.merge(options)
90
+ end
91
+
92
+ def client_region_option
93
+ if @info['region']
94
+ {region: @info['region']}
95
+ else
96
+ {}
97
+ end
98
+ end
99
+ end
100
+ end
@@ -1,17 +1,30 @@
1
1
  require "aws-sdk-dynamodb"
2
2
  require "aws-sdk-s3"
3
+ require "aws-sdk-secretsmanager"
4
+ require "aws-sdk-ssm"
3
5
 
4
6
  module TerraspacePluginAws
5
7
  module Clients
6
8
  extend Memoist
9
+ include Options
7
10
 
8
11
  def s3
9
- Aws::S3::Client.new
12
+ Aws::S3::Client.new(client_options)
10
13
  end
11
14
  memoize :s3
12
15
 
16
+ def secretsmanager
17
+ Aws::SecretsManager::Client.new(client_options)
18
+ end
19
+ memoize :secretsmanager
20
+
21
+ def ssm
22
+ Aws::SSM::Client.new(client_options)
23
+ end
24
+ memoize :ssm
25
+
13
26
  def dynamodb
14
- Aws::DynamoDB::Client.new
27
+ Aws::DynamoDB::Client.new(client_options)
15
28
  end
16
29
  memoize :dynamodb
17
30
  end
@@ -3,13 +3,10 @@ require "s3-secure"
3
3
  class TerraspacePluginAws::Interfaces::Backend
4
4
  class Base
5
5
  include TerraspacePluginAws::Clients
6
+ include TerraspacePluginAws::Logging
6
7
 
7
8
  def initialize(info)
8
9
  @info = info
9
10
  end
10
-
11
- def logger
12
- Terraspace.logger
13
- end
14
11
  end
15
12
  end
@@ -2,6 +2,27 @@ require "s3-secure"
2
2
 
3
3
  class TerraspacePluginAws::Interfaces::Backend::Bucket
4
4
  module Secure
5
+ # Why the retry logic?
6
+ #
7
+ # When using profile or role_arn in the terraform backend it the ruby aws sdk
8
+ # assumes the profile or role.
9
+ # In doing so, it errors when the s3-secure library calls s3_client.get_bucket_location
10
+ #
11
+ # https://github.com/boltops-tools/s3-secure/blob/d2c8e9eba745a75d094a3c566bd5fe47476d3638/lib/s3_secure/aws_services/s3.rb#L43
12
+ #
13
+ # Here's an example stack trace of the error:
14
+ #
15
+ # https://gist.github.com/tongueroo/dd74b67c17433c6f8dd890225104aef9
16
+ #
17
+ # Unsure if this is a terraform backend interfering with the ruby sdk thing (unlikely)
18
+ # Or if it's a general AWS sdk thing.
19
+ # Or if it's how I'm calling the sdk and initializing the client. Maybe an initializing the client early on and it caches it.
20
+ # Unsure. But using this hack instead because life's short.
21
+ #
22
+ # Throwing the retry logic in here fixes the issue. This only happens the when the bucket is brand new.
23
+ # Limiting the retry to only a single attempt.
24
+ #
25
+ @@retries = 0
5
26
  def secure(bucket)
6
27
  c = TerraspacePluginAws::Interfaces::Config.instance.config.s3
7
28
  options = {bucket: bucket, quiet: true}
@@ -10,6 +31,9 @@ class TerraspacePluginAws::Interfaces::Backend::Bucket
10
31
  S3Secure::Versioning::Enable.new(options).run if c.versioning
11
32
  S3Secure::Lifecycle::Add.new(options).run if c.lifecycle
12
33
  S3Secure::AccessLogs::Enable.new(options).run if c.access_logging
34
+ rescue Aws::S3::Errors::AccessDenied => e
35
+ @@retries += 1
36
+ retry unless @@retries > 1
13
37
  end
14
38
  end
15
39
  end
@@ -20,7 +20,7 @@ module TerraspacePluginAws::Interfaces
20
20
  c.s3.enforce_ssl = true
21
21
  c.s3.versioning = true
22
22
  c.s3.lifecycle = true
23
- c.s3.access_logging = true
23
+ c.s3.access_logging = false
24
24
  c.s3.secure_existing = false # run the security controls on existing buckets. by default, only run on newly created bucket the first time
25
25
 
26
26
  c.dynamodb = ActiveSupport::OrderedOptions.new
@@ -0,0 +1,18 @@
1
+ module TerraspacePluginAws::Interfaces::Helper
2
+ class Secret < SecretBase
3
+ def fetch(secret_id)
4
+ value = fetch_value(secret_id)
5
+ value = Base64.strict_encode64(value).strip if @base64
6
+ value
7
+ end
8
+
9
+ def fetch_value(secret_id)
10
+ secret_value = secretsmanager.get_secret_value(secret_id: secret_id)
11
+ secret_value.secret_string
12
+ rescue Aws::SecretsManager::Errors::ResourceNotFoundException => e
13
+ logger.info "WARN: secret_id #{secret_id} not found".color(:yellow)
14
+ logger.info e.message
15
+ "NOT FOUND #{secret_id}" # simple string so Kubernetes YAML is valid
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ require "base64"
2
+
3
+ module TerraspacePluginAws::Interfaces::Helper
4
+ class SecretBase
5
+ include TerraspacePluginAws::Clients
6
+ include TerraspacePluginAws::Logging
7
+
8
+ def initialize(options={})
9
+ @options = options
10
+ @base64 = options[:base64]
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ module TerraspacePluginAws::Interfaces::Helper
2
+ class SSM < SecretBase
3
+ def fetch(name)
4
+ value = fetch_value(name)
5
+ value = Base64.strict_encode64(value).strip if @base64
6
+ value
7
+ end
8
+
9
+ def fetch_value(name)
10
+ resp = ssm.get_parameter(name: name, with_decryption: true)
11
+ resp.parameter.value
12
+ rescue Aws::SSM::Errors::ParameterNotFound => e
13
+ logger.info "WARN: name #{name} not found".color(:yellow)
14
+ logger.info e.message
15
+ "NOT FOUND #{name}" # simple string so tfvars valid
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ module TerraspacePluginAws::Interfaces
2
+ module Helper
3
+ include Terraspace::Plugin::Helper::Interface
4
+
5
+ def aws_secret(name, options={})
6
+ Secret.new(options).fetch(name)
7
+ end
8
+ cache_helper :aws_secret
9
+
10
+ def aws_ssm(name, options={})
11
+ SSM.new(options).fetch(name)
12
+ end
13
+ cache_helper :aws_ssm
14
+ end
15
+ end
@@ -5,7 +5,7 @@ module TerraspacePluginAws::Interfaces
5
5
 
6
6
  # interface method
7
7
  def download
8
- resp = s3.list_objects(bucket: @bucket)
8
+ resp = s3.list_objects(bucket: @bucket, prefix: @folder)
9
9
  resp.contents.each do |content|
10
10
  local_path = "#{@dest}/#{content.key}"
11
11
  FileUtils.mkdir_p(File.dirname(local_path))
@@ -0,0 +1,7 @@
1
+ module TerraspacePluginAws
2
+ module Logging
3
+ def logger
4
+ Terraspace.logger
5
+ end
6
+ end
7
+ end
@@ -1,3 +1,3 @@
1
1
  module TerraspacePluginAws
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.2"
3
3
  end
@@ -22,12 +22,22 @@ module TerraspacePluginAws
22
22
  Interfaces::Config.instance.config
23
23
  end
24
24
 
25
+ @@logger = nil
26
+ def logger
27
+ @@logger ||= Terraspace.logger
28
+ end
29
+
30
+ def logger=(v)
31
+ @@logger = v
32
+ end
33
+
25
34
  extend self
26
35
  end
27
36
 
28
37
  Terraspace::Plugin.register("aws",
29
38
  backend: "s3",
30
39
  config_class: TerraspacePluginAws::Interfaces::Config,
31
- layer_class: TerraspacePluginAws::Interfaces::Layer, # used for layering
40
+ helper_class: TerraspacePluginAws::Interfaces::Helper,
41
+ layer_class: TerraspacePluginAws::Interfaces::Layer,
32
42
  root: File.dirname(__dir__),
33
43
  )
@@ -24,6 +24,8 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.add_dependency "aws-sdk-dynamodb"
26
26
  spec.add_dependency "aws-sdk-s3"
27
+ spec.add_dependency "aws-sdk-secretsmanager"
28
+ spec.add_dependency "aws-sdk-ssm"
27
29
  spec.add_dependency "aws_data"
28
30
  spec.add_dependency "memoist"
29
31
  spec.add_dependency "s3-secure"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: terraspace_plugin_aws
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tung Nguyen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-19 00:00:00.000000000 Z
11
+ date: 2021-12-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-dynamodb
@@ -38,6 +38,34 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: aws-sdk-secretsmanager
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: aws-sdk-ssm
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: aws_data
43
71
  requirement: !ruby/object:Gem::Requirement
@@ -139,6 +167,7 @@ files:
139
167
  - lib/terraspace_plugin_aws.rb
140
168
  - lib/terraspace_plugin_aws/autoloader.rb
141
169
  - lib/terraspace_plugin_aws/clients.rb
170
+ - lib/terraspace_plugin_aws/clients/options.rb
142
171
  - lib/terraspace_plugin_aws/interfaces/backend.rb
143
172
  - lib/terraspace_plugin_aws/interfaces/backend/base.rb
144
173
  - lib/terraspace_plugin_aws/interfaces/backend/bucket.rb
@@ -149,8 +178,13 @@ files:
149
178
  - lib/terraspace_plugin_aws/interfaces/decorator/aws_security_group.rb
150
179
  - lib/terraspace_plugin_aws/interfaces/decorator/base.rb
151
180
  - lib/terraspace_plugin_aws/interfaces/expander.rb
181
+ - lib/terraspace_plugin_aws/interfaces/helper.rb
182
+ - lib/terraspace_plugin_aws/interfaces/helper/secret.rb
183
+ - lib/terraspace_plugin_aws/interfaces/helper/secret_base.rb
184
+ - lib/terraspace_plugin_aws/interfaces/helper/ssm.rb
152
185
  - lib/terraspace_plugin_aws/interfaces/layer.rb
153
186
  - lib/terraspace_plugin_aws/interfaces/summary.rb
187
+ - lib/terraspace_plugin_aws/logging.rb
154
188
  - lib/terraspace_plugin_aws/version.rb
155
189
  - terraspace_plugin_aws.gemspec
156
190
  homepage: https://github.com/boltops-tools/terraspace_plugin_aws
@@ -173,7 +207,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
173
207
  - !ruby/object:Gem::Version
174
208
  version: '0'
175
209
  requirements: []
176
- rubygems_version: 3.1.2
210
+ rubygems_version: 3.2.32
177
211
  signing_key:
178
212
  specification_version: 4
179
213
  summary: Terraspace AWS Plugin