terraspace_plugin_aws 0.3.2 → 0.3.6

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: eb8991c0ca2191cc5ec5d81a91afb2e1db4f7a7bdc8b1542d7e662ee55be2e05
4
- data.tar.gz: f7bf38b1edc49e949643a9df6e70b0826ca5336c18e790513e094054a39ef09c
3
+ metadata.gz: cdced79235a57025ab37b33dbdf98d9146caffaa4f3eac5f40b2a657a99ea3ca
4
+ data.tar.gz: 154369a87b3ffb7fb8290a4b3564108b976e1f7826b0f36fa737b88050e4755c
5
5
  SHA512:
6
- metadata.gz: 4850daa5645efdde37d39ee36fc826bd82993c084d0deabafe83cbd7f182ce630e01d9b5cd7bcde45c366d1e9beeb766cd3f97f411723f9d843a861c4e0a62e5
7
- data.tar.gz: e0229d052f5fb491562683391cec533cddb47cc453d08795200101f20e2b408f51241e1ebe73b4cd5622a5640df06d6236727e4345ae55512e98555f8f95305e
6
+ metadata.gz: 42ebf3fabea658ddb1876aaefa972888e2d2b958239eed92ad9ad9dd3a4d23dea83d67beefbdce28512585a3ceecdb7f904dc08ce8ce87859dcd3e354ada313e
7
+ data.tar.gz: 84c6a6dc52b9f645f5dab2ab1284eccca1d152034106b4a694f42294149d441aaa5bffc0e67cc37e17c5d990758396dadef0761617a3a606372ecc25954d5ea3
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.6] - 2022-01-04
7
+ - [#17](https://github.com/boltops-tools/terraspace_plugin_aws/pull/17) aws_secret and aws_ssm: support expansion automatically
8
+
9
+ ## [0.3.5] - 2021-12-30
10
+ - [#15](https://github.com/boltops-tools/terraspace_plugin_aws/pull/15) block public access support
11
+ - [#16](https://github.com/boltops-tools/terraspace_plugin_aws/pull/16) tagging support for s3 bucket and dynamodb table
12
+
13
+ ## [0.3.4] - 2021-12-30
14
+ - [#13](https://github.com/boltops-tools/terraspace_plugin_aws/pull/13) check aws setup and provide friendly message
15
+ - [#14](https://github.com/boltops-tools/terraspace_plugin_aws/pull/14) fix aws_secret helper
16
+
17
+ ## [0.3.3] - 2021-12-14
18
+ - [#10](https://github.com/boltops-tools/terraspace_plugin_aws/pull/10) implement expand_string? to not expand aws arn values
19
+
6
20
  ## [0.3.2] - 2021-12-14
7
21
  - [#9](https://github.com/boltops-tools/terraspace_plugin_aws/pull/9) support separate aws account for s3 backend bucket
8
22
 
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Terraspace AWS Plugin
2
2
 
3
+ [![BoltOps Badge](https://img.boltops.com/boltops/badges/boltops-badge.png)](https://www.boltops.com)
4
+
3
5
  AWS Cloud support for terraspace.
4
6
 
5
7
  ## Installation
@@ -12,6 +14,8 @@ gem 'terraspace_plugin_aws'
12
14
 
13
15
  ## Configure
14
16
 
17
+ Terraspace Docs: [AWS Terraspace Plugin](https://terraspace.cloud/docs/plugins/aws/)
18
+
15
19
  Optionally configure the plugin. Here's an example `aws.rb` for your terraspace project.
16
20
 
17
21
  config/plugins/aws.rb
@@ -2,8 +2,9 @@ module TerraspacePluginAws::Clients
2
2
  module Options
3
3
  private
4
4
  def client_options
5
+ return {} unless @info # aws_secret helper wont have @info
5
6
  if @info['role_arn']
6
- client_assume_role_config
7
+ client_assume_role_options
7
8
  else
8
9
  client_default_options
9
10
  end
@@ -31,7 +32,7 @@ module TerraspacePluginAws::Clients
31
32
  # :external_id (String)
32
33
  # :client (STS::Client)
33
34
  #
34
- def client_assume_role_config
35
+ def client_assume_role_options
35
36
  whitelist = %w[
36
37
  assume_role_duration_seconds
37
38
  assume_role_policy
@@ -59,10 +60,7 @@ module TerraspacePluginAws::Clients
59
60
  end
60
61
  assume_role_config.symbolize_keys! # ruby sdk expects symbols for keys
61
62
  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)
63
+ role_credentials = Aws::AssumeRoleCredentials.new(assume_role_config)
66
64
  {credentials: role_credentials}
67
65
  end
68
66
 
@@ -23,6 +23,11 @@ module TerraspacePluginAws
23
23
  end
24
24
  memoize :ssm
25
25
 
26
+ def sts
27
+ Aws::STS::Client.new(client_options)
28
+ end
29
+ memoize :sts
30
+
26
31
  def dynamodb
27
32
  Aws::DynamoDB::Client.new(client_options)
28
33
  end
@@ -31,6 +31,7 @@ class TerraspacePluginAws::Interfaces::Backend::Bucket
31
31
  S3Secure::Versioning::Enable.new(options).run if c.versioning
32
32
  S3Secure::Lifecycle::Add.new(options).run if c.lifecycle
33
33
  S3Secure::AccessLogs::Enable.new(options).run if c.access_logging
34
+ S3Secure::PublicAccess::Block.new(options).run if c.block_public_access
34
35
  rescue Aws::S3::Errors::AccessDenied => e
35
36
  @@retries += 1
36
37
  retry unless @@retries > 1
@@ -0,0 +1,44 @@
1
+ class TerraspacePluginAws::Interfaces::Backend::Bucket
2
+ class Tagging
3
+ include TerraspacePluginAws::Clients
4
+ include TerraspacePluginAws::Logging
5
+
6
+ def initialize(bucket)
7
+ @bucket = bucket
8
+ end
9
+
10
+ def tag
11
+ return if tagging.nil? || tagging[:tag_set].empty? # safeguard: dont overwrite current tags
12
+ s3.put_bucket_tagging(bucket: @bucket, tagging: tagging)
13
+ end
14
+
15
+ # Merges existing tag_set structure so always appends tags, wont remove tags.
16
+ # This behavior is consistent with the dynamodb tagging.
17
+ #
18
+ # Example return:
19
+ #
20
+ # {
21
+ # tag_set: [
22
+ # { key: "Key1", value: "Value1" },
23
+ # { key: "Key2", value: "Value2" },
24
+ # ],
25
+ # }
26
+ #
27
+ def tagging
28
+ c = TerraspacePluginAws::Interfaces::Config.instance.config
29
+ tags = !c.s3.tags.empty? ? c.s3.tags : c.tags
30
+ tag_set = tags.map do |k,v|
31
+ {key: k.to_s, value: v}
32
+ end
33
+ return if tag_set == existing_tagging[:tag_set] # return nil so we can avoid the put_bucket_tagging call
34
+ tag_set += existing_tagging[:tag_set]
35
+ { tag_set: tag_set }
36
+ end
37
+
38
+ def existing_tagging
39
+ s3.get_bucket_tagging(bucket: @bucket).to_h
40
+ rescue Aws::S3::Errors::NoSuchTagSet
41
+ {tag_set: []} # normalize return structure
42
+ end
43
+ end
44
+ end
@@ -10,12 +10,14 @@ class TerraspacePluginAws::Interfaces::Backend
10
10
  end
11
11
  if exist?(bucket)
12
12
  logger.debug "Bucket already exist: #{bucket}"
13
- c = TerraspacePluginAws::Interfaces::Config.instance.config.s3
14
- secure(bucket) if c.secure_existing
13
+ c = TerraspacePluginAws::Interfaces::Config.instance.config
14
+ secure(bucket) if c.s3.secure_existing
15
+ tag(bucket) if c.tag_existing
15
16
  else
16
17
  logger.info "Creating bucket: #{bucket}"
17
18
  s3.create_bucket(bucket: bucket)
18
19
  secure(bucket)
20
+ tag(bucket)
19
21
  end
20
22
  end
21
23
 
@@ -30,5 +32,9 @@ class TerraspacePluginAws::Interfaces::Backend
30
32
  logger.error "Bucket might be owned by someone else or is on another one of your AWS accounts."
31
33
  exit 1
32
34
  end
35
+
36
+ def tag(bucket)
37
+ Tagging.new(@info["bucket"]).tag
38
+ end
33
39
  end
34
40
  end
@@ -0,0 +1,15 @@
1
+ class TerraspacePluginAws::Interfaces::Backend
2
+ class Setup < Base
3
+ def check!
4
+ sts.get_caller_identity
5
+ rescue Aws::Errors::MissingCredentialsError => e
6
+ logger.info "ERROR: #{e.class}: #{e.message}".color(:red)
7
+ logger.info <<~EOL
8
+ It doesnt look like AWS credentials and access has been setup.
9
+ Please double check the AWS credentials setup.
10
+ IE: ~/.aws/config and the AWS_PROFILE env variable.
11
+ EOL
12
+ exit 1
13
+ end
14
+ end
15
+ end
@@ -6,6 +6,8 @@ class TerraspacePluginAws::Interfaces::Backend
6
6
 
7
7
  if exist?(table)
8
8
  logger.debug "Table already exist: #{table}"
9
+ c = TerraspacePluginAws::Interfaces::Config.instance.config
10
+ tag_existing(table) if c.tag_existing
9
11
  else
10
12
  logger.info "Creating dynamodb table: #{table}"
11
13
  create_table(table)
@@ -36,6 +38,7 @@ class TerraspacePluginAws::Interfaces::Backend
36
38
  table_name: name,
37
39
  }
38
40
  secure(definition)
41
+ tag(definition)
39
42
  definition
40
43
  end
41
44
 
@@ -64,6 +67,29 @@ class TerraspacePluginAws::Interfaces::Backend
64
67
  definition
65
68
  end
66
69
 
70
+ def tag(definition)
71
+ definition[:tags] = tags unless tags.empty?
72
+ end
73
+
74
+ def tag_existing(table_name)
75
+ return if tags.empty?
76
+ resp = dynamodb.describe_table(table_name: table_name)
77
+ # Always appends tags, wont remove tags.
78
+ dynamodb.tag_resource(
79
+ resource_arn: resp.table.table_arn,
80
+ tags: tags
81
+ )
82
+ end
83
+
84
+ def tags
85
+ c = TerraspacePluginAws::Interfaces::Config.instance.config
86
+ tags = !c.dynamodb.tags.empty? ? c.dynamodb.tags : c.tags
87
+ # Note there is no map! method for Hash
88
+ tags = tags.map do |k,v|
89
+ {key: k.to_s, value: v}
90
+ end
91
+ end
92
+
67
93
  def exist?(name)
68
94
  dynamodb.describe_table(table_name: name)
69
95
  true # table exist
@@ -6,6 +6,7 @@ module TerraspacePluginAws::Interfaces
6
6
  def call
7
7
  return unless TerraspacePluginAws.config.auto_create
8
8
 
9
+ Setup.new(@info).check!
9
10
  Bucket.new(@info).create
10
11
  Table.new(@info).create
11
12
  end
@@ -14,19 +14,24 @@ module TerraspacePluginAws::Interfaces
14
14
  c = ActiveSupport::OrderedOptions.new
15
15
 
16
16
  c.auto_create = true
17
+ c.tags = {} # can set tags for both s3 bucket and dynamodb table with this config
18
+ c.tag_existing = true
17
19
 
18
20
  c.s3 = ActiveSupport::OrderedOptions.new
21
+ c.s3.access_logging = false
22
+ c.s3.block_public_access = true
19
23
  c.s3.encryption = true
20
24
  c.s3.enforce_ssl = true
21
- c.s3.versioning = true
22
25
  c.s3.lifecycle = true
23
- c.s3.access_logging = false
26
+ c.s3.versioning = true
24
27
  c.s3.secure_existing = false # run the security controls on existing buckets. by default, only run on newly created bucket the first time
28
+ c.s3.tags = {} # cannot assign to c.tags here because it's a copy
25
29
 
26
30
  c.dynamodb = ActiveSupport::OrderedOptions.new
27
31
  c.dynamodb.encryption = true
28
32
  c.dynamodb.kms_master_key_id = nil
29
33
  c.dynamodb.sse_type = "KMS"
34
+ c.dynamodb.tags = {} # cannot assign to c.tags here because it's a copy
30
35
 
31
36
  c
32
37
  end
@@ -10,5 +10,9 @@ module TerraspacePluginAws::Interfaces
10
10
  def aws_data
11
11
  $__aws_data ||= AwsData.new
12
12
  end
13
+
14
+ def expand_string?(string)
15
+ !string.include?("arn:")
16
+ end
13
17
  end
14
18
  end
@@ -1,6 +1,7 @@
1
1
  module TerraspacePluginAws::Interfaces::Helper
2
2
  class Secret < SecretBase
3
3
  def fetch(secret_id)
4
+ secret_id = expansion(secret_id) if expand?
4
5
  value = fetch_value(secret_id)
5
6
  value = Base64.strict_encode64(value).strip if @base64
6
7
  value
@@ -13,6 +14,10 @@ module TerraspacePluginAws::Interfaces::Helper
13
14
  logger.info "WARN: secret_id #{secret_id} not found".color(:yellow)
14
15
  logger.info e.message
15
16
  "NOT FOUND #{secret_id}" # simple string so Kubernetes YAML is valid
17
+ rescue Aws::SecretsManager::Errors::ValidationException => e
18
+ logger.info "WARN: secret_id #{secret_id} not found".color(:yellow)
19
+ logger.info e.message
20
+ "INVALID NAME #{secret_id}" # simple string so tfvars valid
16
21
  end
17
22
  end
18
23
  end
@@ -4,10 +4,23 @@ module TerraspacePluginAws::Interfaces::Helper
4
4
  class SecretBase
5
5
  include TerraspacePluginAws::Clients
6
6
  include TerraspacePluginAws::Logging
7
+ extend Memoist
7
8
 
8
- def initialize(options={})
9
+ def initialize(mod, options={})
10
+ @mod = mod
9
11
  @options = options
10
12
  @base64 = options[:base64]
11
13
  end
14
+
15
+ private
16
+ delegate :expansion, to: :expander
17
+ def expander
18
+ TerraspacePluginAws::Interfaces::Expander.new(@mod)
19
+ end
20
+ memoize :expander
21
+
22
+ def expand?
23
+ !(@options[:expansion] == false || @options[:expand] == false)
24
+ end
12
25
  end
13
26
  end
@@ -1,6 +1,10 @@
1
1
  module TerraspacePluginAws::Interfaces::Helper
2
2
  class SSM < SecretBase
3
+ include Terraspace::Compiler::Dsl::Syntax::Mod::Backend
4
+ extend Memoist
5
+
3
6
  def fetch(name)
7
+ name = expansion(name) if expand?
4
8
  value = fetch_value(name)
5
9
  value = Base64.strict_encode64(value).strip if @base64
6
10
  value
@@ -13,6 +17,10 @@ module TerraspacePluginAws::Interfaces::Helper
13
17
  logger.info "WARN: name #{name} not found".color(:yellow)
14
18
  logger.info e.message
15
19
  "NOT FOUND #{name}" # simple string so tfvars valid
20
+ rescue Aws::SSM::Errors::ValidationException => e
21
+ logger.info "WARN: name #{name} not found".color(:yellow)
22
+ logger.info e.message
23
+ "INVALID NAME #{name}" # simple string so tfvars valid
16
24
  end
17
25
  end
18
26
  end
@@ -3,12 +3,12 @@ module TerraspacePluginAws::Interfaces
3
3
  include Terraspace::Plugin::Helper::Interface
4
4
 
5
5
  def aws_secret(name, options={})
6
- Secret.new(options).fetch(name)
6
+ Secret.new(@mod, options).fetch(name)
7
7
  end
8
8
  cache_helper :aws_secret
9
9
 
10
10
  def aws_ssm(name, options={})
11
- SSM.new(options).fetch(name)
11
+ SSM.new(@mod, options).fetch(name)
12
12
  end
13
13
  cache_helper :aws_ssm
14
14
  end
@@ -1,3 +1,3 @@
1
1
  module TerraspacePluginAws
2
- VERSION = "0.3.2"
2
+ VERSION = "0.3.6"
3
3
  end
@@ -28,6 +28,6 @@ Gem::Specification.new do |spec|
28
28
  spec.add_dependency "aws-sdk-ssm"
29
29
  spec.add_dependency "aws_data"
30
30
  spec.add_dependency "memoist"
31
- spec.add_dependency "s3-secure"
31
+ spec.add_dependency "s3-secure", "~> 0.6.1"
32
32
  spec.add_dependency "zeitwerk"
33
33
  end
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.3.2
4
+ version: 0.3.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tung Nguyen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-14 00:00:00.000000000 Z
11
+ date: 2022-01-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-dynamodb
@@ -98,16 +98,16 @@ dependencies:
98
98
  name: s3-secure
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ">="
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0'
103
+ version: 0.6.1
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ">="
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '0'
110
+ version: 0.6.1
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: zeitwerk
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -172,6 +172,8 @@ files:
172
172
  - lib/terraspace_plugin_aws/interfaces/backend/base.rb
173
173
  - lib/terraspace_plugin_aws/interfaces/backend/bucket.rb
174
174
  - lib/terraspace_plugin_aws/interfaces/backend/bucket/secure.rb
175
+ - lib/terraspace_plugin_aws/interfaces/backend/bucket/tagging.rb
176
+ - lib/terraspace_plugin_aws/interfaces/backend/setup.rb
175
177
  - lib/terraspace_plugin_aws/interfaces/backend/table.rb
176
178
  - lib/terraspace_plugin_aws/interfaces/config.rb
177
179
  - lib/terraspace_plugin_aws/interfaces/decorator.rb