modulator 0.2.2 → 0.3.0

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.
@@ -0,0 +1,99 @@
1
+ module StackBuilder
2
+ module LambdaPolicy
3
+ module_function
4
+
5
+ # add inline iam role to lambda, NOTE: use the same role for all lambdas for now
6
+ def add_lambda_iam_role(function_name: nil)
7
+ StackBuilder.stack.add('LambdaRole', Humidifier::IAM::Role.new(
8
+ assume_role_policy_document: {
9
+ "Version" => "2012-10-17",
10
+ 'Statement' => [
11
+ {
12
+ "Action" => ["sts:AssumeRole"],
13
+ "Effect" => "Allow",
14
+ 'Principal' => {
15
+ 'Service' => ["lambda.amazonaws.com"]
16
+ }
17
+ }
18
+ ]
19
+ },
20
+ policies: []
21
+ )
22
+ )
23
+ end
24
+
25
+ def add_policy(policy, **opts)
26
+ StackBuilder.stack.resources['LambdaRole'].properties['policies'] << send(policy, opts)
27
+ end
28
+
29
+ # policy to access cloudwatch
30
+ def cloudwatch(**opts)
31
+ {
32
+ "policy_document" => {
33
+ "Version" => "2012-10-17",
34
+ 'Statement' => [
35
+ {
36
+ "Sid" => "AllowLogCreation",
37
+ "Action" => [
38
+ "logs:CreateLogStream",
39
+ "logs:PutLogEvents",
40
+ ],
41
+ "Effect" => "Allow",
42
+ "Resource" => Humidifier.fn.sub("arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*")
43
+ },
44
+ {
45
+ "Sid" => "AllowLogGroupCreation",
46
+ "Action" => [
47
+ "logs:CreateLogGroup",
48
+ ],
49
+ "Effect" => "Allow",
50
+ "Resource" => "*"
51
+ }
52
+ ]
53
+ },
54
+ "policy_name" => "cloud-watch-access"
55
+ }
56
+ end
57
+
58
+ # policy to access prefixed dynamo tables
59
+ def dynamo_db(**opts)
60
+ prefixes = opts[:prefixes] || []
61
+ prefix_separator = opts[:prefix_separator] || '-'
62
+ wildcard = '*'
63
+ if prefixes.any?
64
+ prefixes.map!{|prefix| prefix == :app_name ? StackBuilder.stack.app_name.dasherize.split('-') : prefix}
65
+ wildcard = "#{(prefixes << '*').join(prefix_separator)}"
66
+ end
67
+ {
68
+ "policy_document" => {
69
+ "Version" => "2012-10-17",
70
+ 'Statement' => [
71
+ {
72
+ "Sid" => "AllowAllActionsOnPrefixedTable",
73
+ "Effect" => "Allow",
74
+ "Action" => [
75
+ "dynamodb:*"
76
+ ],
77
+ "Resource" => Humidifier.fn.sub("arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/#{wildcard}")
78
+ },
79
+ {
80
+ "Sid" => "AdditionalPrivileges",
81
+ "Effect" => "Allow",
82
+ "Action" => [
83
+ "dynamodb:ListTables",
84
+ "dynamodb:DescribeTable"
85
+ ],
86
+ "Resource" => "*"
87
+ }
88
+ ]
89
+ },
90
+ "policy_name" => "dynamo-db-access"
91
+ }
92
+ end
93
+
94
+ # TODO: add access to named secrets
95
+ def secret_manager(**opts)
96
+
97
+ end
98
+ end
99
+ end
@@ -2,28 +2,49 @@ require 'aws-sdk-s3'
2
2
  require 'digest'
3
3
  require 'bundler'
4
4
 
5
- module AwsStackBuilder
5
+ module StackBuilder
6
6
  module_function
7
7
 
8
8
  S3Client = Aws::S3::Client.new
9
9
 
10
- def upload_lambda_handler
11
- bucket_name = Humidifier.ref("S3Bucket").reference
10
+ # bundle gems and upload all for simple lambda apps
11
+ def upload_lambda_files
12
+ puts '- bundling dependencies'
13
+ Bundler.with_clean_env do
14
+ Dir.chdir(app_path) do
15
+ `bundle install`
16
+ `bundle install --deployment --without development`
17
+ end
18
+ end
19
+ FileUtils.remove_dir(app_path.join("vendor/bundle/ruby/#{GEM_PATH_RUBY_VERSION}/cache")) # remove cache dir
20
+ upload_app_layer(sub_dirs: '', add_layer_to_stack: false) # reuse layer upload
21
+ FileUtils.remove_dir(app_path.join('.bundle'))
22
+ FileUtils.remove_dir(app_path.join('vendor'))
23
+ end
24
+
25
+ # generic handler for all lambda
26
+ def upload_generic_lambda_handler
12
27
  lambda_handler_key = LAMBDA_HANDLER_FILE_NAME + '.rb.zip'
28
+ modulator_handler_source = Pathname.new(__FILE__).dirname.parent.join('lambda_handler.rb').read
13
29
  source = <<~SOURCE
14
- require 'modulator/lambda/aws_lambda_handler'
30
+ # DO NOT EDIT THIS FILE
31
+
32
+ #{modulator_handler_source}
15
33
  Dir.chdir('/opt/ruby/lib')
16
34
  SOURCE
17
35
 
18
36
  existing_handler = S3Client.get_object(
19
- bucket: bucket,
37
+ bucket: s3_bucket,
20
38
  key: lambda_handler_key
21
39
  ) rescue false # not found
22
40
 
23
- existing_source = Zip::InputStream.open(existing_handler.body) do |zip_file|
24
- zip_file.get_next_entry
25
- zip_file.read
26
- end if existing_handler
41
+ if existing_handler
42
+ existing_source = Zip::InputStream.open(existing_handler.body) do |zip_file|
43
+ zip_file.get_next_entry
44
+ zip_file.read
45
+ end
46
+ self.lambda_handler_s3_object_version = existing_handler.version_id
47
+ end
27
48
 
28
49
  if existing_source != source
29
50
  puts '- uploading generic lambda handler'
@@ -31,11 +52,12 @@ module AwsStackBuilder
31
52
  zip.put_next_entry LAMBDA_HANDLER_FILE_NAME + '.rb'
32
53
  zip.print source
33
54
  end
34
- S3Client.put_object(
35
- bucket: bucket,
55
+ new_handler = S3Client.put_object(
56
+ bucket: s3_bucket,
36
57
  key: lambda_handler_key,
37
58
  body: source_zip_file.tap(&:rewind).read
38
59
  )
60
+ self.lambda_handler_s3_object_version = new_handler.version_id
39
61
  end
40
62
  end
41
63
 
@@ -51,7 +73,7 @@ module AwsStackBuilder
51
73
  new_checksum = Digest::MD5.hexdigest(File.read(app_path.join('Gemfile.lock')))
52
74
 
53
75
  zip_file_name = app_dir + '_gems.zip'
54
- gems_path = app_path.join(hidden_dir, 'gems')
76
+ gems_path = app_path.join(hidden_dir, 'gems')
55
77
  gems_zip_path = app_path.join(hidden_dir, zip_file_name)
56
78
 
57
79
  if old_checksum != new_checksum
@@ -61,14 +83,14 @@ module AwsStackBuilder
61
83
  # bundle gems
62
84
  Bundler.with_clean_env do
63
85
  Dir.chdir(app_path) do
64
- `bundle install --path=./#{hidden_dir}/gems --clean`
86
+ `bundle install --path=./#{hidden_dir}/gems --clean --without development`
65
87
  end
66
88
  end
67
89
  ZipFileGenerator.new(gems_path, gems_zip_path).write
68
90
 
69
91
  # upload zipped file
70
92
  gem_layer = S3Client.put_object(
71
- bucket: bucket,
93
+ bucket: s3_bucket,
72
94
  key: zip_file_name,
73
95
  body: gems_zip_path.read
74
96
  )
@@ -77,7 +99,7 @@ module AwsStackBuilder
77
99
  gems_zip_path.delete
78
100
  else
79
101
  puts '- using existing gems layer'
80
- gem_layer = S3Client.get_object(bucket: bucket, key: zip_file_name)
102
+ gem_layer = S3Client.get_object(bucket: s3_bucket, key: zip_file_name)
81
103
  end
82
104
 
83
105
  add_layer(
@@ -88,48 +110,53 @@ module AwsStackBuilder
88
110
  )
89
111
  end
90
112
 
91
- def upload_app_layer
113
+ def upload_app_layer(sub_dirs: 'ruby/lib', add_layer_to_stack: true)
92
114
  zip_file_name = app_dir + '.zip'
93
115
  app_zip_path = app_path.join(hidden_dir, zip_file_name)
94
116
 
95
- # copy app code to ruby/lib in use outside temp dir
117
+ # copy app code to ruby/lib in outside temp dir
96
118
  temp_dir_name = '.modulator_temp'
97
- ruby_lib_dirs = 'ruby/lib'
119
+ temp_sub_dirs = sub_dirs
98
120
  temp_path = app_path.parent.join(temp_dir_name)
99
- temp_path.join(ruby_lib_dirs).mkpath
100
- FileUtils.copy_entry app_path, temp_path.join(ruby_lib_dirs)
121
+ temp_path.join(temp_sub_dirs).mkpath
122
+ FileUtils.copy_entry app_path, temp_path.join(temp_sub_dirs)
101
123
 
102
124
  # calculate checksum for app folder
103
125
  checksum_path = app_path.join(hidden_dir, 'app_checksum')
104
126
  old_checksum = (checksum_path.read rescue nil)
105
- new_checksum = checksum(app_path)
127
+ new_checksum = Utils.checksum(app_path)
106
128
 
107
129
  if old_checksum != new_checksum
108
- puts '- uploading app layer'
130
+ puts '- uploading app files'
109
131
  checksum_path.write(new_checksum)
110
132
  ZipFileGenerator.new(temp_path, app_zip_path).write
111
133
  # upload zipped file
112
134
  app_layer = S3Client.put_object(
113
- bucket: bucket,
135
+ bucket: s3_bucket,
114
136
  key: zip_file_name,
115
137
  body: app_zip_path.read
116
138
  )
117
139
  # delete zipped file
118
140
  app_zip_path.delete
119
141
  else
120
- puts '- using existing app layer'
121
- app_layer = S3Client.get_object(bucket: bucket, key: zip_file_name)
142
+ puts '- using existing app files'
143
+ app_layer = S3Client.get_object(bucket: s3_bucket, key: zip_file_name)
122
144
  end
123
145
 
124
146
  # delete temp dir
125
147
  FileUtils.remove_dir(temp_path)
126
148
 
127
- add_layer(
128
- name: app_name,
129
- description: "App source. MD5: #{new_checksum}",
130
- s3_key: zip_file_name,
131
- s3_object_version: app_layer.version_id
132
- )
149
+ if add_layer_to_stack
150
+ add_layer(
151
+ name: app_name,
152
+ description: "App source. MD5: #{new_checksum}",
153
+ s3_key: zip_file_name,
154
+ s3_object_version: app_layer.version_id
155
+ )
156
+ else # for simple lambda apps
157
+ self.lambda_handler_s3_key = zip_file_name
158
+ self.lambda_handler_s3_object_version = app_layer.version_id
159
+ end
133
160
  end
134
161
 
135
162
  # add layer
@@ -139,17 +166,11 @@ module AwsStackBuilder
139
166
  layer_name: name,
140
167
  description: description,
141
168
  content: {
142
- s3_bucket: bucket,
169
+ s3_bucket: s3_bucket,
143
170
  s3_key: s3_key,
144
171
  s3_object_version: s3_object_version
145
172
  }
146
173
  )
147
174
  )
148
175
  end
149
-
150
- def checksum(dir)
151
- files = Dir["#{dir}/**/*"].reject{|f| File.directory?(f)}
152
- content = files.map{|f| File.read(f)}.join
153
- Digest::MD5.hexdigest(content)
154
- end
155
176
  end
@@ -1,3 +1,3 @@
1
1
  module Modulator
2
- VERSION = "0.2.2"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -4,7 +4,7 @@ require 'zip'
4
4
  # the patched classes are used only in tests and tools
5
5
  class String
6
6
  def camelize
7
- split('_').collect do |word|
7
+ gsub('-', '_').split('_').collect do |word|
8
8
  word[0] = word[0].upcase
9
9
  word
10
10
  end.join
@@ -56,6 +56,12 @@ module Utils
56
56
  def load_json(path)
57
57
  JSON.parse(File.read(path))
58
58
  end
59
+
60
+ def checksum(dir)
61
+ files = Dir["#{dir}/**/*"].reject{|f| File.directory?(f)}
62
+ content = files.map{|f| File.read(f)}.join
63
+ Digest::MD5.hexdigest(content)
64
+ end
59
65
  end
60
66
 
61
67
  # NOTE: this code is taken from https://github.com/rubyzip/rubyzip examples
@@ -29,10 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.add_development_dependency "rack-test", "~> 1.1"
30
30
 
31
31
  # stack builder
32
- # custom source is not supported by bundler, it is added to gemfile
33
- # spec.add_dependency "humidifier", github: 'damir/humidifier'
34
- spec.add_dependency "aws-sdk-s3"
35
- spec.add_dependency "aws-sdk-cloudformation"
32
+ spec.add_dependency "humidifier"
36
33
  spec.add_dependency "rubyzip"
37
34
 
38
35
  # local gateway
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: modulator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Damir Roso
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-05-01 00:00:00.000000000 Z
11
+ date: 2019-06-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -67,21 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1.1'
69
69
  - !ruby/object:Gem::Dependency
70
- name: aws-sdk-s3
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :runtime
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: aws-sdk-cloudformation
70
+ name: humidifier
85
71
  requirement: !ruby/object:Gem::Requirement
86
72
  requirements:
87
73
  - - ">="
@@ -160,6 +146,7 @@ files:
160
146
  - ".gitignore"
161
147
  - ".rspec"
162
148
  - ".travis.yml"
149
+ - CHANGELOG.md
163
150
  - CODE_OF_CONDUCT.md
164
151
  - Gemfile
165
152
  - Gemfile.lock
@@ -171,9 +158,11 @@ files:
171
158
  - lib/modulator.rb
172
159
  - lib/modulator/gateway/gateway.rb
173
160
  - lib/modulator/gateway/routes/console.rb
174
- - lib/modulator/lambda/aws_lambda_handler.rb
175
- - lib/modulator/lambda/aws_stack_builder.rb
176
- - lib/modulator/lambda/aws_stack_uploader.rb
161
+ - lib/modulator/gateway_event.json
162
+ - lib/modulator/lambda_handler.rb
163
+ - lib/modulator/stack/builder.rb
164
+ - lib/modulator/stack/policies.rb
165
+ - lib/modulator/stack/uploader.rb
177
166
  - lib/modulator/version.rb
178
167
  - lib/utils.rb
179
168
  - modulator.gemspec
@@ -1,225 +0,0 @@
1
- require 'humidifier'
2
- require_relative 'aws_stack_uploader'
3
-
4
- module AwsStackBuilder
5
- module_function
6
-
7
- RUBY_VERSION = 'ruby2.5'
8
- GEM_PATH = '/opt/ruby/2.5.0'
9
- LAMBDA_HANDLER_FILE_NAME = 'lambda-handler'
10
-
11
- class << self
12
- attr_accessor :app_name, :stack, :api_gateway_deployment, :gateway_id
13
- attr_accessor :app_path, :app_dir, :hidden_dir, :bucket, :stack_opts
14
- end
15
-
16
- def init(app_name:, bucket:, **stack_opts)
17
- puts 'Initializing stack'
18
- @app_name = app_name.camelize
19
- @bucket = bucket
20
- @app_path = Pathname.getwd
21
- @app_dir = app_path.basename.to_s
22
- @hidden_dir = '.modulator'
23
- @stack_opts = stack_opts
24
- @stack = Humidifier::Stack.new(name: @app_name, aws_template_format_version: '2010-09-09')
25
-
26
- # api stage
27
- @stack.add_parameter('ApiGatewayStageName', description: 'Gateway deployment stage', type: 'String', default: 'v1')
28
-
29
- add_api_gateway
30
- add_api_gateway_deployment
31
- add_lambda_iam_role
32
- upload_files
33
- extend_stack_instance(@stack)
34
- @stack
35
- end
36
-
37
- def upload_files
38
- upload_lambda_handler
39
- puts 'Generating layers'
40
- app_path.join(hidden_dir).mkpath
41
- upload_gems_layer
42
- upload_app_layer
43
- end
44
-
45
- # helpers
46
- def extend_stack_instance(stack)
47
- stack.instance_eval do
48
- def add_lambda_endpoint(**opts) # gateway:, mod:, wrapper: {}, env: {}, settings: {}
49
- # add lambda
50
- lambda = AwsStackBuilder.add_lambda(opts)
51
- # add api resources
52
- AwsStackBuilder.add_api_gateway_resources(gateway: opts[:gateway], lambda: lambda)
53
- end
54
- end
55
- end
56
-
57
- # gateway
58
- def add_api_gateway
59
- @gateway_id = 'ApiGateway'
60
- @stack.add(gateway_id, Humidifier::ApiGateway::RestApi.new(name: app_name, description: app_name + ' API'))
61
- end
62
-
63
- # gateway deployment
64
- def add_api_gateway_deployment
65
- @api_gateway_deployment = Humidifier::ApiGateway::Deployment.new(
66
- rest_api_id: Humidifier.ref(gateway_id),
67
- stage_name: Humidifier.ref("ApiGatewayStageName")
68
- )
69
- @stack.add('ApiGatewayDeployment', @api_gateway_deployment)
70
- @stack.add_output('ApiGatewayInvokeURL',
71
- value: Humidifier.fn.sub("https://${#{gateway_id}}.execute-api.${AWS::Region}.amazonaws.com/${ApiGatewayStageName}"),
72
- description: 'API root url',
73
- export_name: app_name + 'RootUrl'
74
- )
75
- @api_gateway_deployment.depends_on = []
76
- end
77
-
78
- # lambda function
79
- def add_lambda(gateway:, mod:, wrapper: {}, env: {}, settings: {})
80
- lambda_config = {}
81
- name_parts = mod[:name].split('::')
82
- {gateway: gateway, module: mod, wrapper: wrapper}.each do |env_group_prefix, env_group|
83
- env_group.each{|env_key, env_value| lambda_config["#{env_group_prefix}_#{env_key}"] = env_value}
84
- end
85
-
86
- lambda_function = Humidifier::Lambda::Function.new(
87
- description: "Lambda for #{mod[:name]}.#{mod[:method]}",
88
- function_name: [app_name.dasherize, name_parts, mod[:method]].flatten.map(&:downcase).join('-'),
89
- handler: "#{LAMBDA_HANDLER_FILE_NAME}.AwsLambdaHandler.call",
90
- environment: {
91
- variables: env
92
- .reduce({}){|env_as_string, (k, v)| env_as_string.update(k.to_s => v.to_s)}
93
- .merge(lambda_config)
94
- .merge('GEM_PATH' => GEM_PATH, 'app_dir' => app_dir)
95
- },
96
- role: Humidifier.fn.get_att(['LambdaRole', 'Arn']),
97
- timeout: settings[:timeout] || stack_opts[:timeout] || 15,
98
- memory_size: settings[:memory_size] || stack_opts[:memory_size] || 128,
99
- runtime: RUBY_VERSION,
100
- code: {
101
- s3_bucket: bucket,
102
- s3_key: LAMBDA_HANDLER_FILE_NAME + '.rb.zip'
103
- },
104
- layers: [
105
- Humidifier.ref(app_name + 'Layer'),
106
- Humidifier.ref(app_name + 'GemsLayer')
107
- ]
108
- )
109
- id = ['Lambda', name_parts, mod[:method].capitalize].join
110
- @stack.add(id, lambda_function)
111
- add_lambda_invoke_permission(id: id, gateway: gateway)
112
- id
113
- end
114
-
115
- # invoke permission
116
- def add_lambda_invoke_permission(id:, gateway:)
117
- arn_path_matcher = gateway[:path].split('/').each_with_object([]) do |fragment, matcher|
118
- fragment = '*' if fragment.start_with?(':')
119
- matcher << fragment
120
- end.join('/')
121
- @stack.add('LambdaPermission', Humidifier::Lambda::Permission.new(
122
- action: "lambda:InvokeFunction",
123
- function_name: Humidifier.fn.get_att([id, 'Arn']),
124
- principal: "apigateway.amazonaws.com",
125
- source_arn: Humidifier.fn.sub("arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${#{gateway_id}}/*/#{gateway[:verb]}/#{arn_path_matcher}")
126
- )
127
- )
128
- end
129
-
130
- # gateway method
131
- def add_api_gateway_resources(gateway:, lambda:)
132
-
133
- # example: calculator/algebra/:x/:y/sum -> module name, args, method name
134
- path = gateway[:path].split('/')
135
-
136
- # root resource
137
- root_resource = path.shift
138
- @stack.add(root_resource.camelize, Humidifier::ApiGateway::Resource.new(
139
- rest_api_id: Humidifier.ref(AwsStackBuilder.gateway_id),
140
- parent_id: Humidifier.fn.get_att(["ApiGateway", "RootResourceId"]),
141
- path_part: root_resource
142
- )
143
- )
144
-
145
- # args and method name are nested resources
146
- parent_resource = root_resource.camelize
147
- path.each do |fragment|
148
- if fragment.start_with?(':')
149
- fragment = fragment[1..-1]
150
- dynamic_fragment = "{#{fragment}}"
151
- end
152
- @stack.add(parent_resource + fragment.camelize, Humidifier::ApiGateway::Resource.new(
153
- rest_api_id: Humidifier.ref(AwsStackBuilder.gateway_id),
154
- parent_id: Humidifier.ref(parent_resource),
155
- path_part: dynamic_fragment || fragment
156
- )
157
- )
158
- parent_resource = parent_resource + fragment.camelize
159
- end
160
-
161
- # attach lambda to last resource
162
- id = 'EndpointFor' + (gateway[:path].gsub(':', '').gsub('/', '_')).camelize
163
- @stack.add(id, Humidifier::ApiGateway::Method.new(
164
- authorization_type: 'NONE',
165
- http_method: gateway[:verb].to_s.upcase,
166
- integration: {
167
- integration_http_method: 'POST',
168
- type: "AWS_PROXY",
169
- uri: Humidifier.fn.sub([
170
- "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations",
171
- 'lambdaArn' => Humidifier.fn.get_att([lambda, 'Arn'])
172
- ])
173
- },
174
- rest_api_id: Humidifier.ref(gateway_id),
175
- resource_id: Humidifier.ref(parent_resource) # last evaluated resource
176
- )
177
- )
178
-
179
- # deployment depends on the method
180
- @api_gateway_deployment.depends_on << id
181
- end
182
-
183
- def add_lambda_iam_role(function_name: nil)
184
- @stack.add('LambdaRole', Humidifier::IAM::Role.new(
185
- assume_role_policy_document: {
186
- 'Version' => "2012-10-17",
187
- 'Statement' => [
188
- {
189
- 'Action' => ["sts:AssumeRole"],
190
- 'Effect' => "Allow",
191
- 'Principal' => {
192
- 'Service' => ["lambda.amazonaws.com"]
193
- }
194
- }
195
- ]
196
- },
197
- policies: [
198
- {
199
- 'policy_document' => {
200
- 'Version' => "2012-10-17",
201
- 'Statement' => [
202
- {
203
- 'Action' => [
204
- "logs:CreateLogStream",
205
- "logs:PutLogEvents",
206
- ],
207
- 'Effect' => "Allow",
208
- 'Resource' => Humidifier.fn.sub("arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*")
209
- },
210
- {
211
- 'Action' => [
212
- "logs:CreateLogGroup",
213
- ],
214
- 'Effect' => "Allow",
215
- 'Resource' => "*"
216
- }
217
- ]
218
- },
219
- 'policy_name' => "cloud-watch-access"
220
- }
221
- ]
222
- )
223
- )
224
- end
225
- end