cfn_monitor 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 34238326509c8f6011a05378837272162e03c4e0b203a740f7cc9b5969aec650
4
+ data.tar.gz: 771cffe9fcd1a9191d183cc2a4b2414429ed1fe599ff9be1f79ff3f01c08ea13
5
+ SHA512:
6
+ metadata.gz: 43a4608773c8c40f26b328f36f2d3ece7dc88aab0ad67e56388ad114f252f1ef37a9cc7ad60c4e4b73c3077cf7dce21de247d40646146b7b1f7e76b81439d11b
7
+ data.tar.gz: c9edf51029dd38f6b31c30b0ed132375e62a62200d2dd3703a6e0a8acb934483f7dc3616f52687759d897251fb5c3f932bef9c2245b3cf8e9712fc6128ef1303
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ *.gem
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,18 @@
1
+ sudo: required
2
+ dist: trusty
3
+ language: ruby
4
+ rvm:
5
+ - 2.5
6
+ script:
7
+ - bundle install
8
+ - bundle exec rake spec
9
+ - gem build cfn_monitor.gemspec
10
+ - gem install cfn_monitor-*.gem
11
+ - cfn_monitor --version
12
+ deploy:
13
+ provider: rubygems
14
+ api_key: "${RUBYGEMS_API_KEY}"
15
+ gem: cfn_monitor
16
+ on:
17
+ all_branches: true
18
+ condition: $TRAVIS_BRANCH =~ ^develop|master && $TRAVIS_EVENT_TYPE =~ ^push|api$ && $TRAVIS_REPO_SLUG == "base2Services/cfn-monitor"
data/Dockerfile ADDED
@@ -0,0 +1,7 @@
1
+ FROM ruby:2.5-alpine
2
+
3
+ RUN gem install cfn_monitor
4
+
5
+ WORKDIR /src
6
+
7
+ CMD ["cfn_monitor"]
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in cfn_monitor.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Guslington
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,424 @@
1
+ # cfn-monitor
2
+
3
+ [![Build Status](https://travis-ci.org/base2Services/cfn-monitor.svg?branch=master)](https://travis-ci.org/base2Services/cfn-monitor)
4
+
5
+ ## About
6
+
7
+ CloudWatch monitoring tool can query a cloudformation stack and return
8
+ monitorable resources that can be placed into a config file. This config
9
+ can then be used to generate a cloudformation stack to create and manage
10
+ cloudwatch alarms.
11
+
12
+ It is packaged as a docker container `base2/cfn_monitor` and
13
+ can be run by volume mounting in a local directory to access the config
14
+ or by using within AWS CodePipeline.
15
+
16
+ ## Commands
17
+
18
+ ```bash
19
+ Commands:
20
+ cfn_monitor --version, -v # print the version
21
+ cfn_monitor deploy # Deploys gerenated cfn templates to S3 bucket
22
+ cfn_monitor generate # Generate monitoring cloudformation templates
23
+ cfn_monitor help [COMMAND] # Describe available commands or one specific command
24
+ cfn_monitor query # Queries a cloudformation stack for monitorable resources
25
+ ```
26
+
27
+ ## Run With Docker
28
+
29
+ The docker image will manage the runtime environment and dependencies.
30
+ You can pass in your AWS credentials and set the region and profile with environment variables.
31
+
32
+ Example
33
+ ```bash
34
+ docker run -it --rm \
35
+ -v $(pwd):/src \
36
+ -v $HOME/.aws:/root/.aws \
37
+ -e AWS_REGION=us-east-1 \
38
+ -e AWS_PROFILE=default \
39
+ base2/cfn_monitor cfn_monitor <command> [parameters]
40
+ ```
41
+
42
+ ## Configuration files
43
+
44
+ There are 2 config files that can be utilised to configure your cloudwatch alarms.
45
+
46
+ - alarms.yaml - Configure the resources you want to monitor
47
+ - template.yaml - Create or override alarm templates
48
+
49
+ The bellow config structure allows for multiple monitoring stacks to be kept with
50
+ a single code repository separated by directories parameterised as `<application>`.
51
+
52
+ ```
53
+ .
54
+ ├── _application_1
55
+ | ├── alarms.yaml
56
+ | └── template.yaml
57
+ └── _application_2
58
+ ├── alarms.yaml
59
+ └── template.yaml
60
+ ```
61
+
62
+ ## Generate
63
+
64
+ The generate command takes you alarm configuration and turns it into
65
+ cloudformation templates to be deployed into AWS.
66
+
67
+ ```bash
68
+ Usage:
69
+ cfn_monitor generate
70
+
71
+ Options:
72
+ a, [--application=APPLICATION] # application name
73
+
74
+ Description:
75
+ Generates cloudformation templates from the alarm configuration and output to the output/ directory.
76
+ ```
77
+
78
+ ### alarms.yml
79
+
80
+ This file is used to configure the AWS resources you want to monitor with CloudWatch.
81
+
82
+ ```YAML
83
+ source_bucket: [Name of S3 bucket where CloudFormation templates will be deployed]
84
+ source_region: [Region of source_bucket]
85
+
86
+ resources:
87
+ [nested stack name].[resource name]: [template name]
88
+ ```
89
+
90
+ Example:
91
+
92
+ ```YAML
93
+ source_bucket: source.example.com
94
+
95
+ resources:
96
+ RDSStack.RDS: RDSInstance
97
+ ```
98
+
99
+ #### Resources
100
+ Resources are referenced by the CloudFormation logical resource ID used to create them. Nested stacks are also referenced by their CloudFormation logical resource ID. See example above.
101
+
102
+ #### Target group configuration:
103
+ Target group alarms in CloudWatch require dimensions for both the target group and its associated load balancer.
104
+ To configure a target group alarm provide the logical ID of the target group (including any stacks it's nested under) followed by "/", followed by the logical ID of the load balancer (also including any stacks it's nested under).
105
+
106
+ Example:
107
+ ```YAML
108
+ resources:
109
+ LoadBalancerStack.WebDefTargetGroup/LoadBalancerStack.WebLoadBalancer: ApplicationELBTargetGroup
110
+ ```
111
+
112
+ #### Custom Metrics
113
+ Custom metrics are configured with a similar syntax to resources. Use `metrics` instead of `resources`.
114
+
115
+ Example:
116
+
117
+ ```YAML
118
+ metrics:
119
+ MyCustomMetric: MyCustomMetricTemplate
120
+ ```
121
+
122
+ #### Endpoints
123
+ HTTP endpoint monitoring and alerting is enabled by configuring resources under `endpoints`. Each endpoint will create a cloudwatch event, scheduled to trigger the `aws-lambda-http-check` lambda function deployed with this stack. Alarms will be configured (based on the specified template) to alert on the cloudwatch metrics generated by the lambda function.
124
+
125
+ Example:
126
+
127
+ ```YAML
128
+ endpoints:
129
+ http://www.base2services.com:
130
+ template: HttpCheck
131
+ statusCode: 200
132
+ bodyRegex: 'DevOps'
133
+ ```
134
+
135
+ ```YAML
136
+ endpoints:
137
+ http://www.base2services.com:
138
+ template: HttpCheck
139
+ statusCode: 200
140
+ bodyRegex: 'DevOps'
141
+ payload: id_=123
142
+ method: POST
143
+ ```
144
+
145
+ Supported parameters:
146
+
147
+ Key | Value | Default
148
+ --- | --- | ---
149
+ statusCode | The expected response code | 200
150
+ bodyRegex | A regex expected in the response body | Disabled
151
+ timeOut | A timeout value for the endpoint monitoring | 120 seconds
152
+ scheduleExpression | A cron expression used to schedule the endpoint monitoring | Every minute
153
+ environments | A string or array of environment names. Monitoring will only be deployed for these environments (if specified) | All environments
154
+
155
+
156
+ #### Multiple templates
157
+ You can specify multiple templates for the resource by providing a list/array. You may want to do this if you want to deploy some custom alarms in addition to the default alarms for a resource.
158
+
159
+ Example:
160
+ ```YAML
161
+ resources:
162
+ RDSStack.RDS: [ 'RDSInstance', 'MyRDSInstance' ]
163
+ ```
164
+ or
165
+ ```YAML
166
+ resources:
167
+ RDSStack.RDS:
168
+ - RDSInstance
169
+ - MyRDSInstance
170
+ ```
171
+
172
+ #### Auto generate alarms config for resources
173
+ You can query an existing stack for monitorable resources using the `query` command.
174
+ This will provide a list of resources in the correct config syntax,
175
+ including the nested stacks and the default templates for those resources.
176
+
177
+ Example:
178
+
179
+ ```bash
180
+ Usage:
181
+ cfn_monitor query
182
+
183
+ Options:
184
+ a, [--application=APPLICATION] # application name
185
+ s, [--stack=STACK] # cfn stack name
186
+
187
+ Description:
188
+ This will provide a list of resources in the correct config syntax,
189
+ including the nested stacks and the default templates for those resources.
190
+ ```
191
+
192
+ Make sure you query a prod sized stack so that all conditional resources are included.
193
+ The output will list all monitorable resources found in the stack, the coverage your current `alarms.yml` config provides, and a list of any resources missing from your current `alarms.yml` config.
194
+
195
+ #### Templates
196
+ The "template" value you specify for a resource refers to either a default templates, or a custom/override template in your own `templates.yml`. This template can contain multiple alarms. The example below shows the default `RDSInstance` template, which has 2 alarms (`FreeStorageSpaceCrit` and `FreeStorageSpaceTask`). Using the `RDSInstance` template in this example will create 2 CloudWatch alarms for the `RDS` resource in the `RDSStack` nested stack.
197
+
198
+ Example: `alarms.yml`
199
+ ```YAML
200
+ resources:
201
+ RDSStack.RDS: RDSInstance
202
+ ```
203
+ Example: `templates.yml`
204
+ ```YAML
205
+ templates:
206
+ RDSInstance: # AWS::RDS::DBInstance
207
+ FreeStorageSpaceCrit:
208
+ AlarmActions: crit
209
+ Namespace: AWS/RDS
210
+ MetricName: FreeStorageSpace
211
+ ComparisonOperator: LessThanThreshold
212
+ DimensionsName: DBInstanceIdentifier
213
+ Statistic: Minimum
214
+ Threshold: 50000000000
215
+ Threshold.development: 10000000000
216
+ EvaluationPeriods: 1
217
+ FreeStorageSpaceTask:
218
+ AlarmActions: task
219
+ Namespace: AWS/RDS
220
+ MetricName: FreeStorageSpace
221
+ ComparisonOperator: LessThanThreshold
222
+ DimensionsName: DBInstanceIdentifier
223
+ Statistic: Minimum
224
+ Threshold: 100000000000
225
+ Threshold.development: 20000000000
226
+ EvaluationPeriods: 1
227
+ ```
228
+
229
+ #### Globally overriding a template
230
+
231
+ You can override a default template in your own `templates.yml` file if all instances of a particular resource require a non standard configuration.
232
+
233
+ Example:
234
+ ```YAML
235
+ templates:
236
+ RDSInstance:
237
+ FreeStorageSpaceCrit:
238
+ Threshold: 80000000000
239
+ ```
240
+
241
+ This configuration will be merged over the default `RDSInstance` template resulting in the following:
242
+
243
+ ```YAML
244
+ templates:
245
+ RDSInstance:
246
+ FreeStorageSpaceCrit:
247
+ AlarmActions: crit
248
+ Namespace: AWS/RDS
249
+ MetricName: FreeStorageSpace
250
+ ComparisonOperator: LessThanThreshold
251
+ DimensionsName: DBInstanceIdentifier
252
+ Statistic: Minimum
253
+ Threshold: 80000000000
254
+ Threshold.development: 10000000000
255
+ EvaluationPeriods: 1
256
+ FreeStorageSpaceTask:
257
+ AlarmActions: task
258
+ Namespace: AWS/RDS
259
+ MetricName: FreeStorageSpace
260
+ ComparisonOperator: LessThanThreshold
261
+ DimensionsName: DBInstanceIdentifier
262
+ Statistic: Minimum
263
+ Threshold: 100000000000
264
+ Threshold.development: 20000000000
265
+ EvaluationPeriods: 1
266
+ ```
267
+
268
+ #### Create a custom template
269
+ If the default template for your resource is completely inappropriate, you can create your own custom template in the `monitoring/templates.yml` file.
270
+
271
+ Example:
272
+
273
+ ```YAML
274
+ templates:
275
+ MyRDSInstance:
276
+ DatabaseConnections:
277
+ AlarmActions: crit
278
+ Namespace: AWS/RDS
279
+ MetricName: DatabaseConnections
280
+ ComparisonOperator: MoreThanThreshold
281
+ DimensionsName: DBInstanceIdentifier
282
+ Statistic: Average
283
+ Threshold: 20
284
+ EvaluationPeriods: 5
285
+ ```
286
+
287
+ #### Inherit a template
288
+ If you have multiple instances of a particular resource and you want to adjust the configuration for only some of them, you can create your own custom template which inherits the configuration of a default template.
289
+
290
+ Example:
291
+ ```YAML
292
+ templates:
293
+ MyRDSInstance:
294
+ template: RDSInstance
295
+ FreeStorageSpaceCrit:
296
+ Threshold: 80000000000
297
+ ```
298
+ The above example creates a new template `MyRDSInstance` which can now be used by one or many resources. The `MyRDSInstance` template inherits all of the alarms and configuration from `RDSInstance`, but sets `Threshold` to `80000000000` for the `FreeStorageSpaceCrit` alarm.
299
+
300
+ #### Environment type mappings
301
+ You can create environment type mappings if alarm configurations need to differ between different environment types. This may be useful in situations where development type environments are running different resource quantities or sizes.
302
+
303
+ Example:
304
+ ```YAML
305
+ templates:
306
+ RDSInstance:
307
+ FreeStorageSpaceCrit:
308
+ Threshold: 40000000000
309
+ Threshold.development: 20000000000
310
+ Threshold.staging: 30000000000
311
+ EvaluationPeriods: 5
312
+ ```
313
+ The above example shows different `Threshold` values for `EnvironmentType` values of `production` (default), `development` or `staging`.
314
+ Any value can be specified using the `.envType` syntax and the necessary mappings and `EnvironmentType` will be generated when rendered.
315
+ The `EvaluationPeriods` value for `development` and `staging` type environments will be `5` in the above example as no `.envType` values where provided for this parameter.
316
+
317
+ Supported Parameters:
318
+ Parameter | Mapping support
319
+ --- | ---
320
+ ActionsEnabled | true
321
+ AlarmActions | false
322
+ AlarmDescription | false
323
+ ComparisonOperator | true
324
+ Dimensions | false
325
+ EvaluateLowSampleCountPercentile | false
326
+ EvaluationPeriods | true
327
+ ExtendedStatistic | false
328
+ InsufficientDataActions | false
329
+ MetricName | true
330
+ Namespace | true
331
+ OKActions | false
332
+ Period | true
333
+ Statistic | true
334
+ Threshold | true
335
+ TreatMissingData | true
336
+ Unit | false
337
+
338
+ #### Template variables
339
+
340
+ The following variables can be used in templates:
341
+
342
+ Variable Key | Variable Value
343
+ --- | ---
344
+ ${name} | Metric/Resource Name (from alarms.yml)
345
+ ${metric} | Metric Name (from alarms.yml)
346
+ ${resource} | Resource Name (from alarms.yml)
347
+ ${templateName} | Template Name (from templates.yml)
348
+ ${alarmName} | Alarm Name (from templates.yml)
349
+
350
+ Example:
351
+
352
+ `alarms.yml`
353
+ ```YAML
354
+ metrics:
355
+ Metric1: MyCustomMetric
356
+ ```
357
+ `templates.yml`
358
+ ```YAML
359
+ templates:
360
+ MyCustomMetric:
361
+ ItemCountHigh:
362
+ MetricName: ${metric}
363
+ AlarmDescription: '#{templateName} #{alarmName} - #{name}'
364
+ ```
365
+ Result:
366
+ ```YAML
367
+ templates:
368
+ MyCustomMetric:
369
+ ItemCountHigh:
370
+ MetricName: Metric1
371
+ AlarmDescription: 'MyCustomMetric ItemCountHigh - Metric1'
372
+ ```
373
+
374
+ #### Alarm Actions
375
+ There are 3 classes of alarm actions: `crit`, `warn` and `task`.
376
+
377
+ Action | Process
378
+ --- | ---
379
+ crit | Alert on-call technician
380
+ warn | Create alarm in pager service but do not alert on-call technician
381
+ task | Create support ticket for investigation
382
+
383
+ An SNS topic is required per alarm action, these topics and their subscriptions are managed outside this stack
384
+
385
+ ### Deployment
386
+
387
+ The rendered CloudFormation templates should be deployed to `[source_bucket]/cloudformation/monitoring/`.
388
+
389
+ ```bash
390
+ Usage:
391
+ cfn_monitor deploy
392
+
393
+ Options:
394
+ a, [--application=APPLICATION] # application name
395
+
396
+ Description:
397
+ Deploys gerenated cloudformation templates to the specified S3 source_bucket
398
+ ```
399
+
400
+ Launch the Monitoring stack in the desired account with the following CloudFormation parameters:
401
+
402
+ Parameter Key | Parameter Value
403
+ --- | ---
404
+ EnvironmentType | `production` / `development` / `custom env type`
405
+ MonitoredStack | The name of the stack you want monitored. EG `prod`
406
+ MonitoringDisabled | `true` for disables alerts, `false` for enabled alerts
407
+ SnsTopicCrit | SNS topic used by crit type alarms
408
+ SnsTopicTask | SNS topic used by task type alarms
409
+ SnsTopicWarn | SNS topic used by warn type alarms
410
+
411
+ ### Disabling Monitoring
412
+ It is possible to globally disable / snooze / downtime all alarms by setting the `MonitoringDisabled` CloudFormation parameter to `true`.
413
+ This will disable alarm actions without removing removing them.
414
+
415
+ ### Disabling and excluding alarms
416
+ To disable or prevent creation of a specific alarm, specify either of the following parameters:
417
+ ```YAML
418
+ templates:
419
+ MyAutoScalingGroup:
420
+ template: AutoScalingGroup
421
+ CPUUtilizationHighBase:
422
+ CreateAlarm: false # Don't create the alarm
423
+ DisableAlarm: true # Create the alarm but disable it
424
+ ```
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "rake"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |t|
5
+ t.pattern = Dir.glob("spec/**/*_spec.rb")
6
+ t.rspec_opts = "--format documentation"
7
+ end
8
+
9
+ task default: :spec
@@ -0,0 +1,39 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "cfn_monitor/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "cfn_monitor"
8
+ spec.version = CfnMonitor::VERSION
9
+ spec.authors = ["Base2Services", "Jared Brook", "Angus Vine"]
10
+ spec.email = ["itsupport@base2services.com"]
11
+
12
+ spec.summary = %q{Configure and generate a cloudwatch monitoring cloudformation stack}
13
+ spec.description = %q{CloudWatch monitoring tool can query a cloudformation stack and return
14
+ monitorable resources that can be placed into a config file. This config
15
+ can then be used to generate a cloudformation stack to create and manage
16
+ cloudwatch alarms.}
17
+ spec.homepage = "https://github.com/base2Services/cfn-monitor/blob/master/README.md"
18
+ spec.license = "MIT"
19
+
20
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
21
+ f.match(%r{^(test|spec|features)/})
22
+ end
23
+ spec.bindir = "exe"
24
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
+ spec.require_paths = ["lib"]
26
+
27
+ spec.add_dependency "thor", "~> 0.19"
28
+ spec.add_dependency "cfndsl", "~> 0.16"
29
+ spec.add_dependency "aws-sdk-cloudformation", "~> 1", "<2"
30
+ spec.add_dependency "aws-sdk-s3", "~> 1", "<2"
31
+ spec.add_dependency "aws-sdk-elasticloadbalancingv2", "~> 1", "<2"
32
+
33
+ spec.add_development_dependency "bundler", "~> 1.16"
34
+ spec.add_development_dependency "rake", "~> 10.0"
35
+ spec.add_development_dependency "rspec", "~> 0.9"
36
+ spec.add_development_dependency "rspec-core", "~> 3.8"
37
+ spec.add_development_dependency "rspec-expectations", "~> 3.8"
38
+ spec.add_development_dependency "rspec-mocks", "~> 3.8"
39
+ end
data/exe/cfn_monitor ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require "cfn_monitor"
3
+
4
+ CfnMonitor::Commands.start( ARGV )
@@ -0,0 +1,47 @@
1
+ require 'aws-sdk-s3'
2
+ require 'yaml'
3
+
4
+ module CfnMonitor
5
+ class Deploy
6
+
7
+ def self.run(options)
8
+
9
+ if !options['application']
10
+ raise "No application specified"
11
+ end
12
+
13
+ application = options['application']
14
+
15
+ custom_alarms_config_file = "#{application}/alarms.yml"
16
+ output_path = "output/#{application}"
17
+ upload_path = "cloudformation/monitoring/#{application}"
18
+
19
+ # Load custom config files
20
+ if File.file?(custom_alarms_config_file)
21
+ custom_alarms_config = YAML.load(File.read(custom_alarms_config_file)) if File.file?(custom_alarms_config_file)
22
+ else
23
+ puts "Failed to load #{custom_alarms_config_file}"
24
+ exit 1
25
+ end
26
+
27
+ puts "-----------------------------------------------"
28
+ s3 = Aws::S3::Client.new(region: custom_alarms_config['source_region'])
29
+ ["#{output_path}/*.json"].each { |path|
30
+ Dir.glob(path) do |file|
31
+ template = File.open(file, 'rb')
32
+ filename = file.gsub("#{output_path}/", "")
33
+ s3.put_object({
34
+ body: template,
35
+ bucket: "#{custom_alarms_config['source_bucket']}",
36
+ key: "#{upload_path}/#{filename}",
37
+ })
38
+ puts "INFO: Copied #{file} to s3://#{custom_alarms_config['source_bucket']}/#{upload_path}/#{filename}"
39
+ end
40
+ }
41
+ puts "-----------------------------------------------"
42
+ puts "Master stack: https://s3.#{custom_alarms_config['source_region']}.amazonaws.com/#{custom_alarms_config['source_bucket']}/#{upload_path}/master.json"
43
+ puts "-----------------------------------------------"
44
+ end
45
+
46
+ end
47
+ end