aws_recon 0.4.4 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48ccf03d964fff678dc732fcf74d4dcd9c7c06293845bbb9a4f54e9e1ce61a24
4
- data.tar.gz: 7c39747a7845fe497be052baae41af8ee45014166d97114abb797d5f6c06e5b9
3
+ metadata.gz: 7481b13d21571402935b0ce2b67a7cdaaf3d3fc245b49f5569ab249b00a80769
4
+ data.tar.gz: d755e86dbe27036c6db5aec7a10497f1cf85c4ad64265f673ec10fd1490d9566
5
5
  SHA512:
6
- metadata.gz: 3b6c26ff3b38dd58c63313b70a609e6b08fe6d16762afb9651bbc81d6cef21d7c46e8b11ad73d40caf0e6abbfd877ef5d5f2fde1536425b70a25afe82b7a4a78
7
- data.tar.gz: a74af8ad1b868895d997110a4688bc88266536ad2187c235ba27a0720e3ccce0d18d6959981ab3e82e40bb738944d1402380761f9c3c65ce33cc1649bfddbc4e
6
+ metadata.gz: a97a2b0b84fd34a79be57dac06caefa77231a7098d2ee221e3d6587d57c51aa181aff4aefeb13bfcfd52578b2f30285ce3e1a2f5f70ed8cff6c37d426f2daaa4
7
+ data.tar.gz: 7fdab7b7ddebb23fd28d28721966ff1d29a7b3a07c351e6319d3c6cdeb08fc4788869b8c30e2ea5f38a180bfe4cbf55dda05206a9985e568d0ea564d1c7eb19b
@@ -0,0 +1,17 @@
1
+ name: check-service-regions
2
+
3
+ on:
4
+ schedule:
5
+ - cron: '40 15 * * *'
6
+
7
+ jobs:
8
+ region-check:
9
+ runs-on: ubuntu-20.04
10
+ steps:
11
+ - name: Checkout
12
+ uses: actions/checkout@v2
13
+ with:
14
+ fetch-depth: 1
15
+ - name: Set version tag
16
+ run: |
17
+ cd utils/aws ; ruby check_region_exclusions.rb
data/.solargraph.yml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ include:
3
+ - "**/*.rb"
4
+ exclude:
5
+ - spec/**/*
6
+ - test/**/*
7
+ - vendor/**/*
8
+ - ".bundle/**/*"
9
+ require: []
10
+ domains: []
11
+ reporters:
12
+ - rubocop
13
+ require_paths: []
14
+ plugins: []
15
+ max_files: 5000
@@ -65,6 +65,17 @@ module AwsRecon
65
65
  @resources.concat(collection) if @options.output_file
66
66
  end
67
67
 
68
+ #
69
+ # Format @resources as either
70
+ #
71
+ def formatted_json
72
+ if @options.jsonl
73
+ @resources.map { |r| JSON.generate(r) }.join("\n")
74
+ else
75
+ @resources.to_json
76
+ end
77
+ end
78
+
68
79
  #
69
80
  # main wrapper
70
81
  #
@@ -112,7 +123,7 @@ module AwsRecon
112
123
  if @options.output_file && !@options.s3
113
124
  puts "Saving resources to #{@options.output_file}.\n\n"
114
125
 
115
- File.write(@options.output_file, @resources.to_json)
126
+ File.write(@options.output_file, formatted_json)
116
127
  end
117
128
 
118
129
  # write output file to S3 bucket
@@ -128,7 +139,7 @@ module AwsRecon
128
139
  # build IO object and gzip it
129
140
  io = StringIO.new
130
141
  gzip_data = Zlib::GzipWriter.new(io)
131
- gzip_data.write(@resources.to_json)
142
+ gzip_data.write(formatted_json)
132
143
  gzip_data.close
133
144
 
134
145
  # send it to S3
@@ -18,7 +18,7 @@ class DynamoDB < Mapper
18
18
 
19
19
  struct = OpenStruct.new(response)
20
20
  struct.type = 'limits'
21
- struct.arn = "arn:aws:dynamodb:#{@region}:#{@account}:limits"
21
+ struct.arn = "arn:aws:dynamodb:#{@region}:#{@account}/limits"
22
22
 
23
23
  resources.push(struct.to_h)
24
24
  end
@@ -29,7 +29,7 @@ class EC2 < Mapper
29
29
  struct = OpenStruct.new
30
30
  struct.attributes = response.account_attributes.map(&:to_h)
31
31
  struct.type = 'account'
32
- struct.arn = "arn:aws::#{@account}"
32
+ struct.arn = "arn:aws:ec2::#{@account}:attributes/account_attributes"
33
33
 
34
34
  resources.push(struct.to_h)
35
35
  end
@@ -45,6 +45,7 @@ class EC2 < Mapper
45
45
 
46
46
  struct = OpenStruct.new(response.to_h)
47
47
  struct.type = 'ebs_encryption_settings'
48
+ struct.arn = "arn:aws:ec2:#{@region}:#{@account}:settings/ebs_encryption_settings"
48
49
 
49
50
  resources.push(struct.to_h)
50
51
  end
@@ -63,7 +64,7 @@ class EC2 < Mapper
63
64
  reservation.instances.each do |instance|
64
65
  struct = OpenStruct.new(instance.to_h)
65
66
  struct.type = 'instance'
66
- struct.arn = instance.instance_id # no true ARN
67
+ struct.arn = "arn:aws:ec2:#{@region}:#{@account}:instance/#{instance.instance_id}" # no true ARN
67
68
  struct.reservation_id = reservation.reservation_id
68
69
 
69
70
  # collect instance user_data
@@ -95,7 +96,7 @@ class EC2 < Mapper
95
96
  response.vpcs.each do |vpc|
96
97
  struct = OpenStruct.new(vpc.to_h)
97
98
  struct.type = 'vpc'
98
- struct.arn = vpc.vpc_id # no true ARN
99
+ struct.arn = "arn:aws:ec2:#{@region}:#{@account}:vpc/#{vpc.vpc_id}" # no true ARN
99
100
  struct.flow_logs = @client
100
101
  .describe_flow_logs({ filter: [{ name: 'resource-id', values: [vpc.vpc_id] }] })
101
102
  .flow_logs.first.to_h
@@ -113,7 +114,7 @@ class EC2 < Mapper
113
114
  response.security_groups.each do |security_group|
114
115
  struct = OpenStruct.new(security_group.to_h)
115
116
  struct.type = 'security_group'
116
- struct.arn = security_group.group_id # no true ARN
117
+ struct.arn = "arn:aws:ec2:#{@region}:#{@account}:security_group/#{security_group.group_id}" # no true ARN
117
118
 
118
119
  resources.push(struct.to_h)
119
120
  end
@@ -128,7 +129,7 @@ class EC2 < Mapper
128
129
  response.network_interfaces.each do |network_interface|
129
130
  struct = OpenStruct.new(network_interface.to_h)
130
131
  struct.type = 'network_interface'
131
- struct.arn = network_interface.network_interface_id # no true ARN
132
+ struct.arn = "arn:aws:ec2:#{@region}:#{@account}:network_interface/#{network_interface.network_interface_id}" # no true ARN
132
133
 
133
134
  resources.push(struct.to_h)
134
135
  end
@@ -143,7 +144,7 @@ class EC2 < Mapper
143
144
  response.network_acls.each do |network_acl|
144
145
  struct = OpenStruct.new(network_acl.to_h)
145
146
  struct.type = 'network_acl'
146
- struct.arn = network_acl.network_acl_id # no true ARN
147
+ struct.arn = "arn:aws:ec2:#{@region}:#{@account}:network_acl/#{network_acl.network_acl_id}" # no true ARN
147
148
 
148
149
  resources.push(struct.to_h)
149
150
  end
@@ -173,7 +174,7 @@ class EC2 < Mapper
173
174
  response.addresses.each do |address|
174
175
  struct = OpenStruct.new(address.to_h)
175
176
  struct.type = 'eip_address'
176
- struct.arn = address.allocation_id
177
+ struct.arn = "arn:aws:ec2:#{@region}:#{@account}:eip_address/#{address.allocation_id}" # no true ARN
177
178
 
178
179
  resources.push(struct.to_h)
179
180
  end
@@ -188,7 +189,7 @@ class EC2 < Mapper
188
189
  response.nat_gateways.each do |gateway|
189
190
  struct = OpenStruct.new(gateway.to_h)
190
191
  struct.type = 'nat_gateway'
191
- struct.arn = gateway.nat_gateway_id # no true ARN
192
+ struct.arn = "arn:aws:ec2:#{@region}:#{@account}:nat_gateway/#{gateway.nat_gateway_id}" # no true ARN
192
193
 
193
194
  resources.push(struct.to_h)
194
195
  end
@@ -203,7 +204,7 @@ class EC2 < Mapper
203
204
  response.internet_gateways.each do |gateway|
204
205
  struct = OpenStruct.new(gateway.to_h)
205
206
  struct.type = 'internet_gateway'
206
- struct.arn = gateway.internet_gateway_id # no true ARN
207
+ struct.arn = "arn:aws:ec2:#{@region}:#{@account}:internet_gateway/#{gateway.internet_gateway_id}" # no true ARN
207
208
 
208
209
  resources.push(struct.to_h)
209
210
  end
@@ -218,7 +219,7 @@ class EC2 < Mapper
218
219
  response.route_tables.each do |table|
219
220
  struct = OpenStruct.new(table.to_h)
220
221
  struct.type = 'route_table'
221
- struct.arn = table.route_table_id # no true ARN
222
+ struct.arn = "arn:aws:ec2:#{@region}:#{@account}:route_table/#{table.route_table_id}" # no true ARN
222
223
 
223
224
  resources.push(struct.to_h)
224
225
  end
@@ -233,7 +234,7 @@ class EC2 < Mapper
233
234
  response.images.each do |image|
234
235
  struct = OpenStruct.new(image.to_h)
235
236
  struct.type = 'image'
236
- struct.arn = image.image_id # no true ARN
237
+ struct.arn = "arn:aws:ec2:#{@region}:#{@account}:image/#{image.image_id}" # no true ARN
237
238
 
238
239
  resources.push(struct.to_h)
239
240
  end
@@ -248,7 +249,7 @@ class EC2 < Mapper
248
249
  response.snapshots.each do |snapshot|
249
250
  struct = OpenStruct.new(snapshot.to_h)
250
251
  struct.type = 'snapshot'
251
- struct.arn = snapshot.snapshot_id # no true ARN
252
+ struct.arn = "arn:aws:ec2:#{@region}:#{@account}:snapshot/#{snapshot.snapshot_id}" # no true ARN
252
253
  struct.create_volume_permissions = @client.describe_snapshot_attribute({
253
254
  attribute: 'createVolumePermission',
254
255
  snapshot_id: snapshot.snapshot_id
@@ -267,7 +268,7 @@ class EC2 < Mapper
267
268
  response.flow_logs.each do |flow_log|
268
269
  struct = OpenStruct.new(flow_log.to_h)
269
270
  struct.type = 'flow_log'
270
- struct.arn = flow_log.flow_log_id # no true ARN
271
+ struct.arn = "arn:aws:ec2:#{@region}:#{@account}:flow_log/#{flow_log.flow_log_id}" # no true ARN
271
272
 
272
273
  resources.push(struct.to_h)
273
274
  end
@@ -282,7 +283,7 @@ class EC2 < Mapper
282
283
  response.volumes.each do |volume|
283
284
  struct = OpenStruct.new(volume.to_h)
284
285
  struct.type = 'volume'
285
- struct.arn = volume.volume_id # no true ARN
286
+ struct.arn = "arn:aws:ec2:#{@region}:#{@account}:volume/#{volume.volume_id}" # no true ARN
286
287
 
287
288
  resources.push(struct.to_h)
288
289
  end
@@ -297,7 +298,7 @@ class EC2 < Mapper
297
298
  response.vpn_gateways.each do |gateway|
298
299
  struct = OpenStruct.new(gateway.to_h)
299
300
  struct.type = 'vpn_gateway'
300
- struct.arn = gateway.vpn_gateway_id # no true ARN
301
+ struct.arn = "arn:aws:ec2:#{@region}:#{@account}:vpn_gateway/#{gateway.vpn_gateway_id}" # no true ARN
301
302
 
302
303
  resources.push(struct.to_h)
303
304
  end
@@ -312,7 +313,7 @@ class EC2 < Mapper
312
313
  response.vpc_peering_connections.each do |peer|
313
314
  struct = OpenStruct.new(peer.to_h)
314
315
  struct.type = 'peering_connection'
315
- struct.arn = peer.vpc_peering_connection_id # no true ARN
316
+ struct.arn = "arn:aws:ec2:#{@region}:#{@account}:peering_connection/#{peer.vpc_peering_connection_id}" # no true ARN
316
317
 
317
318
  resources.push(struct.to_h)
318
319
  end
@@ -50,7 +50,7 @@ class IAM < Mapper
50
50
  policy_document: p.policy_document.parse_policy
51
51
  }
52
52
  end
53
- end
53
+ end
54
54
 
55
55
  resources.push(struct.to_h)
56
56
  end
@@ -20,6 +20,7 @@ class Parser
20
20
  :output_file,
21
21
  :output_format,
22
22
  :threads,
23
+ :jsonl,
23
24
  :collect_user_data,
24
25
  :skip_slow,
25
26
  :skip_credential_report,
@@ -35,8 +36,8 @@ class Parser
35
36
  aws_regions = ['global'].concat(Aws::EC2::Client.new.describe_regions.regions.map(&:region_name))
36
37
  end
37
38
  rescue Aws::Errors::ServiceError => e
38
- puts "\nAWS Error: #{e.code}\n\n"
39
- exit
39
+ warn "\nAWS Error: #{e.code}\n\n"
40
+ exit(1)
40
41
  end
41
42
 
42
43
  aws_services = YAML.load(File.read(SERVICES_CONFIG_FILE), symbolize_names: true)
@@ -55,6 +56,7 @@ class Parser
55
56
  false,
56
57
  false,
57
58
  false,
59
+ false,
58
60
  false
59
61
  )
60
62
 
@@ -116,6 +118,11 @@ class Parser
116
118
  args.threads = threads.to_i if (0..Parser::MAX_THREADS).include?(threads.to_i)
117
119
  end
118
120
 
121
+ # output NDJSON/JSONL format
122
+ opts.on('-l', '--json-lines', 'Output NDJSON/JSONL format (default: false)') do
123
+ args.jsonl = true
124
+ end
125
+
119
126
  # collect EC2 instance user data
120
127
  opts.on('-u', '--user-data', 'Collect EC2 instance user data (default: false)') do
121
128
  args.collect_user_data = true
@@ -106,7 +106,7 @@
106
106
  - name: SecretsManager
107
107
  alias: sm
108
108
  - name: SecurityHub
109
- alias: sh
109
+ alias: securityhub
110
110
  excluded_regions:
111
111
  - ap-northeast-3
112
112
  - name: Support
@@ -1,3 +1,3 @@
1
1
  module AwsRecon
2
- VERSION = "0.4.4"
2
+ VERSION = "0.5.2"
3
3
  end
data/readme.md CHANGED
@@ -54,13 +54,13 @@ To run locally, first install the gem:
54
54
 
55
55
  ```
56
56
  $ gem install aws_recon
57
- Fetching aws_recon-0.4.0.gem
57
+ Fetching aws_recon-0.4.5.gem
58
58
  Fetching aws-sdk-3.0.1.gem
59
59
  Fetching parallel-1.20.1.gem
60
60
  ...
61
61
  Successfully installed aws-sdk-3.0.1
62
62
  Successfully installed parallel-1.20.1
63
- Successfully installed aws_recon-0.4.0
63
+ Successfully installed aws_recon-0.4.5
64
64
  ```
65
65
 
66
66
  Or add it to your Gemfile using `bundle`:
@@ -72,7 +72,7 @@ Resolving dependencies...
72
72
  ...
73
73
  Using aws-sdk 3.0.1
74
74
  Using parallel-1.20.1
75
- Using aws_recon 0.4.0
75
+ Using aws_recon 0.4.5
76
76
  ```
77
77
 
78
78
  ## Usage
@@ -249,7 +249,7 @@ Most users will want to limit collection to relevant services and regions. Runni
249
249
  ```
250
250
  $ aws_recon -h
251
251
 
252
- AWS Recon - AWS Inventory Collector (0.4.0)
252
+ AWS Recon - AWS Inventory Collector (0.4.5)
253
253
 
254
254
  Usage: aws_recon [options]
255
255
  -r, --regions [REGIONS] Regions to scan, separated by comma (default: all)
@@ -261,6 +261,7 @@ Usage: aws_recon [options]
261
261
  -o, --output [OUTPUT] Specify output file (default: output.json)
262
262
  -f, --format [FORMAT] Specify output format (default: aws)
263
263
  -t, --threads [THREADS] Specify max threads (default: 8, max: 128)
264
+ -l, --json-lines Output NDJSON/JSONL format (default: false)
264
265
  -u, --user-data Collect EC2 instance user data (default: false)
265
266
  -z, --skip-slow Skip slow operations (default: false)
266
267
  -g, --skip-credential-report Skip generating IAM credential report (default: false)
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Check regional service availability against services.yaml exclusions.
5
+ #
6
+ require 'net/http'
7
+ require 'json'
8
+ require 'yaml'
9
+
10
+ TS = Time.now.to_i
11
+ URL = "https://api.regional-table.region-services.aws.a2z.com/index.json?timestamp=#{TS}000"
12
+
13
+ region_exclusion_mistmatch = nil
14
+
15
+ #
16
+ # load current AWS Recon regions
17
+ #
18
+ recon_services = YAML.safe_load(File.read('../../lib/aws_recon/services.yaml'))
19
+ abort('Errors loading AWS Recon services') unless recon_services.is_a?(Array)
20
+
21
+ #
22
+ # load current AWS regions (non-gov, non-cn)
23
+ #
24
+ regions = YAML.safe_load(File.read('regions.yaml'))
25
+ abort('Errors loading regions') unless regions['Regions']
26
+
27
+ all_regions = regions['Regions'].map { |r| r['RegionName'] }
28
+
29
+ #
30
+ # get service/price list from AWS
31
+ #
32
+ uri = URI(URL)
33
+ res = Net::HTTP.get_response(uri)
34
+ abort('Error loading AWS services from API') unless res.code == '200'
35
+
36
+ map = {}
37
+
38
+ #
39
+ # load service region availability
40
+ #
41
+ data = res.body
42
+ json = JSON.parse(data)
43
+
44
+ # iterate through AWS provided services & regions
45
+ json['prices'].each do |p|
46
+ at = p['attributes']
47
+ service_name = at['aws:serviceName']
48
+ service_id, service_region = p['id'].split(':')
49
+
50
+ # skip this service unless AWS Recon already has exclusions
51
+ next unless recon_services.filter { |s| s['alias'] == service_id }&.length&.positive?
52
+
53
+ if map.key?(service_name)
54
+ map[service_name]['regions'] << service_region
55
+ else
56
+ map[service_name] = {
57
+ 'id' => service_id,
58
+ 'regions' => [service_region]
59
+ }
60
+ end
61
+ end
62
+
63
+ # iterate through the services AWS Recon knows about
64
+ map.sort.each do |k, v|
65
+ service_excluded_regions = all_regions.reject { |r| v['regions'].include?(r) }
66
+
67
+ aws_recon_service = recon_services.filter { |s| s['alias'] == v['id'] }&.first
68
+ aws_recon_service_excluded_regions = aws_recon_service['excluded_regions'] || []
69
+
70
+ # move on if AWS Recon region exclusions match AWS service region exclusions
71
+ next unless service_excluded_regions.sort != aws_recon_service_excluded_regions.sort
72
+
73
+ region_exclusion_mistmatch = true
74
+
75
+ puts "#{k} (#{v['id']})"
76
+
77
+ # determine the direction of the exclusion mismatch
78
+ if (service_excluded_regions - aws_recon_service_excluded_regions).length.positive?
79
+ puts " + missing region exclusion: #{(service_excluded_regions - aws_recon_service_excluded_regions).join(', ')}"
80
+ else
81
+ puts " - unnecessary region exclusion: #{(aws_recon_service_excluded_regions - service_excluded_regions).join(', ')}"
82
+ end
83
+ end
84
+
85
+ # exit code 1 if we have any mismatches
86
+ exit 1 if region_exclusion_mistmatch
@@ -0,0 +1,43 @@
1
+ Regions:
2
+ - Endpoint: ec2.af-south-1.amazonaws.com
3
+ RegionName: af-south-1
4
+ - Endpoint: ec2.eu-north-1.amazonaws.com
5
+ RegionName: eu-north-1
6
+ - Endpoint: ec2.ap-south-1.amazonaws.com
7
+ RegionName: ap-south-1
8
+ - Endpoint: ec2.eu-west-3.amazonaws.com
9
+ RegionName: eu-west-3
10
+ - Endpoint: ec2.eu-west-2.amazonaws.com
11
+ RegionName: eu-west-2
12
+ - Endpoint: ec2.eu-south-1.amazonaws.com
13
+ RegionName: eu-south-1
14
+ - Endpoint: ec2.eu-west-1.amazonaws.com
15
+ RegionName: eu-west-1
16
+ - Endpoint: ec2.ap-northeast-3.amazonaws.com
17
+ RegionName: ap-northeast-3
18
+ - Endpoint: ec2.ap-northeast-2.amazonaws.com
19
+ RegionName: ap-northeast-2
20
+ - Endpoint: ec2.me-south-1.amazonaws.com
21
+ RegionName: me-south-1
22
+ - Endpoint: ec2.ap-northeast-1.amazonaws.com
23
+ RegionName: ap-northeast-1
24
+ - Endpoint: ec2.sa-east-1.amazonaws.com
25
+ RegionName: sa-east-1
26
+ - Endpoint: ec2.ca-central-1.amazonaws.com
27
+ RegionName: ca-central-1
28
+ - Endpoint: ec2.ap-east-1.amazonaws.com
29
+ RegionName: ap-east-1
30
+ - Endpoint: ec2.ap-southeast-1.amazonaws.com
31
+ RegionName: ap-southeast-1
32
+ - Endpoint: ec2.ap-southeast-2.amazonaws.com
33
+ RegionName: ap-southeast-2
34
+ - Endpoint: ec2.eu-central-1.amazonaws.com
35
+ RegionName: eu-central-1
36
+ - Endpoint: ec2.us-east-1.amazonaws.com
37
+ RegionName: us-east-1
38
+ - Endpoint: ec2.us-east-2.amazonaws.com
39
+ RegionName: us-east-2
40
+ - Endpoint: ec2.us-west-1.amazonaws.com
41
+ RegionName: us-west-1
42
+ - Endpoint: ec2.us-west-2.amazonaws.com
43
+ RegionName: us-west-2
@@ -22,6 +22,7 @@ resource "aws_ecs_task_definition" "aws_recon_task" {
22
22
  "--verbose",
23
23
  "--format",
24
24
  "custom",
25
+ "--json-lines",
25
26
  "--s3-bucket",
26
27
  "${aws_s3_bucket.aws_recon.bucket}:${data.aws_region.current.name}",
27
28
  "--regions",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aws_recon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.4
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Larsen
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-04-06 00:00:00.000000000 Z
12
+ date: 2021-04-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aws-sdk
@@ -163,10 +163,12 @@ extensions: []
163
163
  extra_rdoc_files: []
164
164
  files:
165
165
  - ".github/stale.yml"
166
+ - ".github/workflows/check-aws-regions.yml"
166
167
  - ".github/workflows/docker-build.yml"
167
168
  - ".github/workflows/smoke-test.yml"
168
169
  - ".gitignore"
169
170
  - ".rubocop.yml"
171
+ - ".solargraph.yml"
170
172
  - Dockerfile
171
173
  - Gemfile
172
174
  - LICENSE.txt
@@ -244,6 +246,8 @@ files:
244
246
  - lib/aws_recon/services.yaml
245
247
  - lib/aws_recon/version.rb
246
248
  - readme.md
249
+ - utils/aws/check_region_exclusions.rb
250
+ - utils/aws/regions.yaml
247
251
  - utils/cloudformation/aws-recon-cfn-template.yml
248
252
  - utils/terraform/cloudwatch.tf
249
253
  - utils/terraform/ecs.tf