cucloud 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YzQ3Y2ZmNTA0ZGNkMjllZWY2ZDAzNTZhMWU2OTJkMDYxMGQ3Mjg5ZQ==
4
+ NzdhZDFlN2ZmYzFjMzhmMmM4ZTBlMTM5MzZiMDM5MmJlODY3MWQyOQ==
5
5
  data.tar.gz: !binary |-
6
- ZTk0NDQ1YWViYjc5YTAzZGZhMjc3MTI5ZTg5ZDE3MzgzMWJjMzM1NQ==
6
+ OWQxMWUyODJjODRhMjFlYWY0OTdlN2QyMDNlYjMxOGEyOWQ4YWY5Mw==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- N2Y2Zjg0YzA2M2YyZWUwNWNkMTAwNjUyZmQ5OTU4N2NjNmZhMDZiODg4NzMz
10
- M2NlYzIzZGIxMjNmNTcyNDhmOThjMmE1ODc2OWRhNGVhODBkNGNjZDA3Mzdl
11
- NzAxZDNhNjcwMjUzMTRhMDMxMjlmNTg2Yjc0MTRjMDA0Mzg3MWI=
9
+ M2NjYjg2MWRhOWMwMTg4NzA3YTRkNDZjNGNhNWMzNDNjZjYyOTU3ZWUwNzM3
10
+ MmY1OWE1ZTczMDEwYWFkNWQxMjZkZGNmYmQ0OGYyZmQ0ODQyZDZmOWIzNGE2
11
+ YjMwNzY0NmE2YzQ3Y2Y4YjdiMTE1ZjIxMGEyYjMzMzA1ZWUwZTQ=
12
12
  data.tar.gz: !binary |-
13
- ZTg4YjMwNTNlYWRmNDljNThlNTRiZDIzMmIwMjUwNmZlYjkwZDU0MjIzMWVh
14
- NjVkZjc0YTc1YTgyZTQzMDg3ZTQxNjcyOTlkMTFiNjkwYTM1Y2FkNTZlYWUz
15
- MDFlZWEwNmY1N2VjZmZkYmM0YzE3OWMyMDMzZjRlMmFkNGZlNzA=
13
+ ZmQxYzg0ZTcxY2I5NDZmMWVlMWQ3YTYyYjdjNjE3N2IwYmRkMDAxZjRkZGU3
14
+ MDEyZDFkZjY5ODI1Mzk2MGQ4ZDBjMTRmY2U5MmE1Zjg4ODI4NWIwYmViNjk0
15
+ ZWI3ZjRlNjIzMTY3ZjY5YWNmNTg0YTFhYjMwNWFhYjE0YjdlZGI=
data/.rubocop.yml CHANGED
@@ -9,3 +9,7 @@ Metrics/MethodLength:
9
9
 
10
10
  Metrics/AbcSize:
11
11
  Max: 50
12
+
13
+ # disable get/set warnings - many of our methods are performing API calls and get_ seems appropriate naming
14
+ Style/AccessorMethodName:
15
+ Enabled: false
data/.travis.yml CHANGED
@@ -13,3 +13,7 @@ deploy:
13
13
  on:
14
14
  tags: true
15
15
  repo: CU-CloudCollab/cucloud_ruby
16
+ notifications:
17
+ hipchat:
18
+ rooms:
19
+ secure: poxd8oy6KvYQe2yfe6vms84iW/HzPAQyt07r1Z2GhEjaW5Oof5Gi/FPl/j6L3im/ymy8KPuCN4MI0ZjglIq0qRuiFJai107uhgxZb0+31DOfM2DL0OFJOWfmm1rI8mLdyfQ1KYYx7115vtE4ZBqxSb3q3o66/3CISGfQR1dmHx8jT1ABGBf5fPLhxvu6BLnxgsdGQWFC/Va7DHhhUb//R0dZIENNy0WOJlaAjgm00Dp96sDHe+h3hzYuNA3ZCU6dos2nyKT0FrNDzcfofCaPkekUkb/Dm8LDup/HXeg4DM7a+MfKcn6NUjUcTx28o+rAggDPpDxIr4f38rsDFKip47CGrO3jhVK0Q1lOO6/gWUnGn1Ec0sB9uCNHlSa860oINw7eU3kC38DwBcspMItRpDo6eBkdh7irEibu14+FtCq/QgHjDghLNfJb/Zw4zo5Ip+yDbmDtdDXA7T7t4DXD/2T4UKKc9FvjTmv224QFi5XQUOiIUDNGjCvhqVDKKZv9KuYspiBy0xoxroGtWR4dqqZwlikvTKsSGIJASTZHlmZ3vfzrAWzHfC0twWZ2kmkM8j6V81lZqCxfv6xRj7fKw68B7LahfuBsQUpgWEnBcZ8qnMyRuIeq7Rm7cqkL/7ZG7cpoCRXaXRMGQZ2sUgZkWQ9RuTFTzaa6jUFZGFpnufs=
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Cucloud ![Build Status](https://api.travis-ci.org/CU-CloudCollab/cucloud_ruby.svg)
1
+ # Cucloud ![Build Status](https://api.travis-ci.org/CU-CloudCollab/cucloud_ruby.svg) [![Gem Version](https://badge.fury.io/rb/cucloud.svg)](https://badge.fury.io/rb/cucloud) [![Coverage Status](https://coveralls.io/repos/github/CU-CloudCollab/cucloud_ruby/badge.svg?branch=master)](https://coveralls.io/github/CU-CloudCollab/cucloud_ruby?branch=master)
2
2
 
3
3
  The cucloud module is intended to serve as a lightweight wrapper around the AWS SDK that can be used to share common functionality across various AWS utilities and tools that we develop at Cornell. Goals:
4
4
 
data/cucloud.gemspec CHANGED
@@ -11,12 +11,12 @@ Gem::Specification.new do |spec|
11
11
 
12
12
  spec.email = ['shawn.bower@gmail.com', 'bmh67@cornell.edu', 'sr523@cornell.edu']
13
13
 
14
- spec.summary = 'The cucloud module is intended to provide functionality requiring more customization than ' \
15
- 'could otherwise be simply accomplished with a cloud specific command line interface, e.g. ' \
16
- 'AWS CLI'
17
- spec.description = 'The cucloud module is intended to provide functionality requiring more customization than ' \
18
- 'could otherwise be simply accomplished with a cloud specific command line interface, e.g. ' \
19
- 'AWS CLI'
14
+ spec.summary = 'The cucloud module is intended to serve as a lightweight wrapper around the AWS SDK that ' \
15
+ 'can be used to share common functionality across various AWS utilities and tools that we ' \
16
+ 'develop at Cornell'
17
+ spec.description = 'The cucloud module is intended to serve as a lightweight wrapper around the AWS SDK that ' \
18
+ 'can be used to share common functionality across various AWS utilities and tools that we ' \
19
+ 'develop at Cornell'
20
20
  spec.homepage = 'https://github.com/CU-CloudCollab/cucloud_ruby'
21
21
 
22
22
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.add_dependency 'uuid', '~> 2.3'
29
29
 
30
30
  spec.add_development_dependency 'bundler', '~> 1.11'
31
+ spec.add_development_dependency 'coveralls'
31
32
  spec.add_development_dependency 'rake', '~> 10.0'
32
33
  spec.add_development_dependency 'rspec', '~> 3.0'
33
34
  spec.add_development_dependency 'rubocop', '~> 0.40.0'
data/lib/cucloud.rb CHANGED
@@ -5,11 +5,32 @@ module Cucloud
5
5
  require 'cucloud/version'
6
6
  require 'cucloud/ec2_utils'
7
7
  require 'cucloud/asg_utils'
8
+ require 'cucloud/ssm_utils'
9
+ require 'cucloud/iam_utils'
8
10
 
9
11
  DEFAULT_REGION = 'us-east-1'.freeze
10
12
 
11
13
  Aws.config = { region: DEFAULT_REGION }
12
14
 
15
+ CORNELL_SAML_X509 = %(<ds:X509Certificate>MIIDSDCCAjCgAwIBAgIVAOZ8NfBem6sHcI7F39sYmD/JG4YDMA0GCSqGSIb3DQEB
16
+ BQUAMCIxIDAeBgNVBAMTF3NoaWJpZHAuY2l0LmNvcm5lbGwuZWR1MB4XDTA5MTEy
17
+ MzE4NTI0NFoXDTI5MTEyMzE4NTI0NFowIjEgMB4GA1UEAxMXc2hpYmlkcC5jaXQu
18
+ Y29ybmVsbC5lZHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCTURo9
19
+ 90uuODo/5ju3GZThcT67K3RXW69jwlBwfn3png75Dhyw9Xa50RFv0EbdfrojH1P1
20
+ 9LyfCjubfsm9Z7FYkVWSVdPSvQ0BXx7zQxdTpE9137qj740tMJr7Wi+iWdkyBQS/
21
+ bCNhuLHeNQor6NXZoBgX8HvLy4sCUb/4v7vbp90HkmP3FzJRDevzgr6PVNqWwNqp
22
+ tZ0vQHSF5D3iBNbxq3csfRGQQyVi729XuWMSqEjPhhkf1UjVcJ3/cG8tWbRKw+W+
23
+ OIm71k+99kOgg7IvygndzzaGDVhDFMyiGZ4njMzEJT67sEq0pMuuwLMlLE/86mSv
24
+ uGwO2Qacb1ckzjodAgMBAAGjdTBzMFIGA1UdEQRLMEmCF3NoaWJpZHAuY2l0LmNv
25
+ cm5lbGwuZWR1hi5odHRwczovL3NoaWJpZHAuY2l0LmNvcm5lbGwuZWR1L2lkcC9z
26
+ aGliYm9sZXRoMB0GA1UdDgQWBBSQgitoP2/rJMDepS1sFgM35xw19zANBgkqhkiG
27
+ 9w0BAQUFAAOCAQEAaFrLOGqMsbX1YlseO+SM3JKfgfjBBL5TP86qqiCuq9a1J6B7
28
+ Yv+XYLmZBy04EfV0L7HjYX5aGIWLDtz9YAis4g3xTPWe1/bjdltUq5seRuksJjyb
29
+ prGI2oAv/ShPBOyrkadectHzvu5K6CL7AxNTWCSXswtfdsuxcKo65tO5TRO1hWlr
30
+ 7Pq2F+Oj2hOvcwC0vOOjlYNe9yRE9DjJAzv4rrZUg71R3IEKNjfOF80LYPAFD2Sp
31
+ p36uB6TmSYl1nBmS5LgWF4EpEuODPSmy4sIV6jl1otuyI/An2dOcNqcgu7tYEXLX
32
+ C8N6DXggDWPtPRdpk96UW45huvXudpZenrcd7A==</ds:X509Certificate>).freeze
33
+
13
34
  def region
14
35
  @region
15
36
  end
@@ -6,9 +6,10 @@ module Cucloud
6
6
  UBUNTU_PATCH_COMMAND = 'apt-get update; apt-get -y upgrade; reboot'.freeze
7
7
  AMAZON_PATCH_COMMAND = 'yum update -y; reboot & disown '.freeze
8
8
 
9
- def initialize(ec2_client = Aws::EC2::Client.new)
9
+ def initialize(ec2_client = Aws::EC2::Client.new, ssm_utils = Cucloud::SSMUtils.new)
10
10
  ## DI for testing purposes
11
11
  @ec2 = ec2_client
12
+ @ssm_utils = ssm_utils
12
13
  end
13
14
 
14
15
  def get_instance(instance)
@@ -82,18 +83,6 @@ module Cucloud
82
83
  end
83
84
  end
84
85
 
85
- def send_patch_command(patch_instances, command)
86
- ssm = Aws::SSM::Client.new(region: 'us-east-1')
87
-
88
- ssm.send_command(instance_ids: patch_instances, # required
89
- document_name: 'AWS-RunShellScript', # required
90
- timeout_seconds: 600,
91
- comment: 'Patch It!',
92
- parameters: {
93
- 'commands' => [command]
94
- })
95
- end
96
-
97
86
  # rubocop:disable Metrics/CyclomaticComplexity
98
87
  # rubocop:disable Metrics/PerceivedComplexity
99
88
  # @todo consider refactoring/breaking out functionality so that complexity metrics pass
@@ -119,8 +108,8 @@ module Cucloud
119
108
  end
120
109
  end
121
110
 
122
- send_patch_command(ubuntu_patch_instances, UBUNTU_PATCH_COMMAND) if ubuntu_patch_instances.any?
123
- send_patch_command(amazon_patch_instances, AMAZON_PATCH_COMMAND) if amazon_patch_instances.any?
111
+ @ssm_utils.send_patch_command(ubuntu_patch_instances, UBUNTU_PATCH_COMMAND) if ubuntu_patch_instances.any?
112
+ @ssm_utils.send_patch_command(amazon_patch_instances, AMAZON_PATCH_COMMAND) if amazon_patch_instances.any?
124
113
 
125
114
  all_instances
126
115
  end
@@ -0,0 +1,177 @@
1
+ module Cucloud
2
+ # Utilities library for interacting with IAM
3
+ class IamUtils
4
+ # Define some error classes
5
+ class UnknownComparisonOperatorError < StandardError
6
+ end
7
+
8
+ def initialize(iam_client = Aws::IAM::Client.new)
9
+ @iam = iam_client
10
+ end
11
+
12
+ # Get the alias set for this account if it exists
13
+ # @return [String] Account alias (nil if not set)
14
+ def get_account_alias
15
+ # https://docs.aws.amazon.com/sdkforruby/api/Aws/IAM/Client.html#list_account_aliases-instance_method
16
+ # https://docs.aws.amazon.com/IAM/latest/UserGuide/console_account-alias.html
17
+ # Per user guide: Account can have only one alias
18
+
19
+ @iam.list_account_aliases.account_aliases[0]
20
+ end
21
+
22
+ # Get report about IAM entity usage and quotas in this account
23
+ # @return [Hash<String,Integer>] A hash of key value pairs containing information about IAM entity usage and quotas.
24
+ def get_account_summary
25
+ # https://docs.aws.amazon.com/sdkforruby/api/Aws/IAM/Client.html#get_account_summary-instance_method
26
+ # return https://docs.aws.amazon.com/sdkforruby/api/Aws/IAM/Types/GetAccountSummaryResponse.html#summary_map-instance_method
27
+ @iam.get_account_summary.summary_map
28
+ end
29
+
30
+ # Does this account's root user have any API keys?
31
+ # @return [Boolean]
32
+ def root_user_has_api_key?
33
+ get_account_summary['AccountAccessKeysPresent'] > 0
34
+ end
35
+
36
+ # Does this account's root user have MFA enabled?
37
+ # @return [Boolean]
38
+ def root_user_mfa_enabled?
39
+ get_account_summary['AccountMFAEnabled'] > 0
40
+ end
41
+
42
+ # Does this account have multiple identity providers configured?
43
+ # @return [Boolean]
44
+ def multiple_providers_configured?
45
+ get_account_summary['Providers'] > 1
46
+ end
47
+
48
+ # Get password policy for this account
49
+ # @return [Aws::IAM::Types::PasswordPolicy]
50
+ def get_account_password_policy
51
+ # https://docs.aws.amazon.com/sdkforruby/api/Aws/IAM/Client.html#get_account_password_policy-instance_method
52
+ @iam.get_account_password_policy.password_policy
53
+ end
54
+
55
+ # Check password policy against an options hash of audit criteria
56
+ #
57
+ # Policy format - Array of checks
58
+ # example input: [{ key: "minimum_password_length", operator: "GT", value: 15 }]
59
+ # example output: [{ key: "minimum_password_length", passes: true }]
60
+ # @param [Array<Hash>] Policy against which to audit
61
+ # @return [Array<Hash>] Results of each audit check
62
+ # rubocop:disable Metrics/CyclomaticComplexity
63
+ # disable complexity check here - doesn't seem worth breaking this function up
64
+ def audit_password_policy(audit_criteria = [])
65
+ policy_hash = get_account_password_policy.to_h
66
+
67
+ audit_array = []
68
+ audit_criteria.each do |check|
69
+ case check[:operator]
70
+ when 'EQ'
71
+ audit_array << {
72
+ key: check[:key],
73
+ passes: policy_hash[check[:key].to_sym].nil? ? false : policy_hash[check[:key].to_sym] == check[:value]
74
+ }
75
+ when 'LTE'
76
+ audit_array << {
77
+ key: check[:key],
78
+ passes: policy_hash[check[:key].to_sym].nil? ? false : policy_hash[check[:key].to_sym] <= check[:value]
79
+ }
80
+ when 'GTE'
81
+ audit_array << {
82
+ key: check[:key],
83
+ passes: policy_hash[check[:key].to_sym].nil? ? false : policy_hash[check[:key].to_sym] >= check[:value]
84
+ }
85
+ else
86
+ raise UnknownComparisonOperatorError.new, "Unknown operator #{check[:operator]}"
87
+ end
88
+ end
89
+
90
+ audit_array
91
+ end
92
+ # rubocop:enable Metrics/CyclomaticComplexity
93
+
94
+ # Get SAML providers configured for this account
95
+ # @return [Array<Hash>] Array of hashes in form { arn: <String>, metadata: <String> }
96
+ def get_saml_providers
97
+ # https://docs.aws.amazon.com/sdkforruby/api/Aws/IAM/Client.html#list_saml_providers-instance_method
98
+ # returns https://docs.aws.amazon.com/sdkforruby/api/Aws/IAM/Types/SAMLProviderListEntry.html
99
+ # https://docs.aws.amazon.com/sdkforruby/api/Aws/IAM/Client.html#get_saml_provider-instance_method
100
+
101
+ provider_array = []
102
+ @iam.list_saml_providers.saml_provider_list.each do |provider|
103
+ provider_array << {
104
+ arn: provider.arn,
105
+ saml_metadata_document: @iam.get_saml_provider(saml_provider_arn: provider.arn).saml_metadata_document
106
+ }
107
+ end
108
+
109
+ provider_array
110
+ end
111
+
112
+ # Is the Cornell SAML Identity Provider configured on this account?
113
+ # @return [Boolean]
114
+ def cornell_provider_configured?
115
+ get_saml_providers.select { |provider| provider[:saml_metadata_document].include? CORNELL_SAML_X509 }.any?
116
+ end
117
+
118
+ # Get users that are configured on this account
119
+ # @return [Array<Hash>] Array of user hashes - base user type + added lookups for convenience
120
+ def get_users
121
+ # https://docs.aws.amazon.com/sdkforruby/api/Aws/IAM/Client.html#list_users-instance_method
122
+ user_array = []
123
+ @iam.list_users.users.each do |user|
124
+ user_array << {
125
+ base_data: user, # https://docs.aws.amazon.com/sdkforruby/api/Aws/IAM/Types/User.html
126
+ has_password: user_has_password?(user.user_name)
127
+ }
128
+ end
129
+ user_array
130
+ end
131
+
132
+ # Does this user have a password configured?
133
+ # @param [String] Username
134
+ # @return [Boolean]
135
+ def user_has_password?(user_name)
136
+ # https://docs.aws.amazon.com/sdkforruby/api/Aws/IAM/Client.html#get_login_profile-instance_method
137
+ password = true
138
+
139
+ begin
140
+ @iam.get_login_profile(user_name: user_name)
141
+ rescue Aws::IAM::Errors::NoSuchEntity
142
+ password = false
143
+ end
144
+
145
+ password
146
+ end
147
+
148
+ # Get access keys for user
149
+ # @param [String] Username
150
+ # @return [Array<Hash>] Array of key hashes - base key data + helper calculations for key age and active/inactive
151
+ def get_user_access_keys(user_name)
152
+ # https://docs.aws.amazon.com/sdkforruby/api/Aws/IAM/Client.html#list_access_keys-instance_method
153
+ keys = []
154
+ @iam.list_access_keys(user_name: user_name).access_key_metadata.each do |key|
155
+ keys << {
156
+ base_data: key,
157
+ active: key.status == 'Active',
158
+ days_old: (Time.now - key.create_date).to_i / (24 * 60 * 60)
159
+ }
160
+ end
161
+
162
+ keys
163
+ end
164
+
165
+ # Get active access keys on account that are older than specified age (in days)
166
+ # @param [Integer] Days old
167
+ # @return [Array<Hash>]
168
+ def get_active_keys_older_than_n_days(n)
169
+ keys = []
170
+ get_users.each do |user|
171
+ keys << get_user_access_keys(user[:base_data].user_name).select { |k| k[:days_old] > n && k[:active] }
172
+ end
173
+
174
+ keys.flatten
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,20 @@
1
+ module Cucloud
2
+ # SSMUtils class - for interacting with the simple server management service
3
+ class SSMUtils
4
+ def initialize(ssm_client = Aws::SSM::Client.new)
5
+ @ssm = ssm_client
6
+ end
7
+
8
+ def send_patch_command(patch_instances, command)
9
+ @ssm.send_command(
10
+ instance_ids: patch_instances, # required
11
+ document_name: 'AWS-RunShellScript', # required
12
+ timeout_seconds: 600,
13
+ comment: 'Patch It!',
14
+ parameters: {
15
+ 'commands' => [command]
16
+ }
17
+ )
18
+ end
19
+ end
20
+ end
@@ -1,5 +1,5 @@
1
1
  module Cucloud
2
2
  # Disable mutable constant warning - freezing this oddly breaks bundler
3
3
  # rubocop:disable Style/MutableConstant
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cucloud
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - sbower
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2016-07-13 00:00:00.000000000 Z
13
+ date: 2016-07-25 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: aws-sdk
@@ -54,6 +54,20 @@ dependencies:
54
54
  - - ~>
55
55
  - !ruby/object:Gem::Version
56
56
  version: '1.11'
57
+ - !ruby/object:Gem::Dependency
58
+ name: coveralls
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ type: :development
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
57
71
  - !ruby/object:Gem::Dependency
58
72
  name: rake
59
73
  requirement: !ruby/object:Gem::Requirement
@@ -110,9 +124,9 @@ dependencies:
110
124
  - - ~>
111
125
  - !ruby/object:Gem::Version
112
126
  version: 0.9.0
113
- description: The cucloud module is intended to provide functionality requiring more
114
- customization than could otherwise be simply accomplished with a cloud specific
115
- command line interface, e.g. AWS CLI
127
+ description: The cucloud module is intended to serve as a lightweight wrapper around
128
+ the AWS SDK that can be used to share common functionality across various AWS utilities
129
+ and tools that we develop at Cornell
116
130
  email:
117
131
  - shawn.bower@gmail.com
118
132
  - bmh67@cornell.edu
@@ -137,6 +151,8 @@ files:
137
151
  - lib/cucloud/asg_utils.rb
138
152
  - lib/cucloud/ec2_utils.rb
139
153
  - lib/cucloud/elb_utils.rb
154
+ - lib/cucloud/iam_utils.rb
155
+ - lib/cucloud/ssm_utils.rb
140
156
  - lib/cucloud/version.rb
141
157
  homepage: https://github.com/CU-CloudCollab/cucloud_ruby
142
158
  licenses: []
@@ -160,7 +176,7 @@ rubyforge_project:
160
176
  rubygems_version: 2.4.5
161
177
  signing_key:
162
178
  specification_version: 4
163
- summary: The cucloud module is intended to provide functionality requiring more customization
164
- than could otherwise be simply accomplished with a cloud specific command line interface,
165
- e.g. AWS CLI
179
+ summary: The cucloud module is intended to serve as a lightweight wrapper around the
180
+ AWS SDK that can be used to share common functionality across various AWS utilities
181
+ and tools that we develop at Cornell
166
182
  test_files: []