aws-insight 0.14.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.
Files changed (114) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +84 -0
  5. data/.travis.yml +19 -0
  6. data/CHANGELOG.md +336 -0
  7. data/CODE_OF_CONDUCT.md +13 -0
  8. data/CONTRIBUTING.md +62 -0
  9. data/Gemfile +19 -0
  10. data/Guardfile +12 -0
  11. data/LICENSE +22 -0
  12. data/README.md +393 -0
  13. data/Rakefile +6 -0
  14. data/bin/terraforming +5 -0
  15. data/contrib/zsh-completion/_terraforming +65 -0
  16. data/lib/terraforming.rb +54 -0
  17. data/lib/terraforming/cli.rb +340 -0
  18. data/lib/terraforming/resource/alb.rb +98 -0
  19. data/lib/terraforming/resource/auto_scaling_group.rb +90 -0
  20. data/lib/terraforming/resource/cloud_watch_alarm.rb +87 -0
  21. data/lib/terraforming/resource/config.rb +98 -0
  22. data/lib/terraforming/resource/db_parameter_group.rb +58 -0
  23. data/lib/terraforming/resource/db_security_group.rb +57 -0
  24. data/lib/terraforming/resource/db_subnet_group.rb +52 -0
  25. data/lib/terraforming/resource/ec2.rb +130 -0
  26. data/lib/terraforming/resource/efs_file_system.rb +60 -0
  27. data/lib/terraforming/resource/eip.rb +66 -0
  28. data/lib/terraforming/resource/elasti_cache_cluster.rb +80 -0
  29. data/lib/terraforming/resource/elasti_cache_subnet_group.rb +56 -0
  30. data/lib/terraforming/resource/elb.rb +203 -0
  31. data/lib/terraforming/resource/iam_group.rb +54 -0
  32. data/lib/terraforming/resource/iam_group_membership.rb +63 -0
  33. data/lib/terraforming/resource/iam_group_policy.rb +71 -0
  34. data/lib/terraforming/resource/iam_instance_profile.rb +54 -0
  35. data/lib/terraforming/resource/iam_policy.rb +63 -0
  36. data/lib/terraforming/resource/iam_policy_attachment.rb +86 -0
  37. data/lib/terraforming/resource/iam_role.rb +56 -0
  38. data/lib/terraforming/resource/iam_role_policy.rb +71 -0
  39. data/lib/terraforming/resource/iam_user.rb +55 -0
  40. data/lib/terraforming/resource/iam_user_policy.rb +71 -0
  41. data/lib/terraforming/resource/internet_gateway.rb +54 -0
  42. data/lib/terraforming/resource/kms_alias.rb +55 -0
  43. data/lib/terraforming/resource/kms_key.rb +84 -0
  44. data/lib/terraforming/resource/launch_configuration.rb +106 -0
  45. data/lib/terraforming/resource/nat_gateway.rb +57 -0
  46. data/lib/terraforming/resource/network_acl.rb +83 -0
  47. data/lib/terraforming/resource/network_interface.rb +68 -0
  48. data/lib/terraforming/resource/rds.rb +74 -0
  49. data/lib/terraforming/resource/redshift.rb +67 -0
  50. data/lib/terraforming/resource/route53_record.rb +106 -0
  51. data/lib/terraforming/resource/route53_zone.rb +89 -0
  52. data/lib/terraforming/resource/route_table.rb +122 -0
  53. data/lib/terraforming/resource/route_table_association.rb +59 -0
  54. data/lib/terraforming/resource/s3.rb +69 -0
  55. data/lib/terraforming/resource/security_group.rb +188 -0
  56. data/lib/terraforming/resource/sns_topic.rb +75 -0
  57. data/lib/terraforming/resource/sns_topic_subscription.rb +83 -0
  58. data/lib/terraforming/resource/sqs.rb +70 -0
  59. data/lib/terraforming/resource/subnet.rb +55 -0
  60. data/lib/terraforming/resource/vpc.rb +67 -0
  61. data/lib/terraforming/resource/vpn_gateway.rb +55 -0
  62. data/lib/terraforming/template/tf/alb.erb +28 -0
  63. data/lib/terraforming/template/tf/auto_scaling_group.erb +28 -0
  64. data/lib/terraforming/template/tf/cloud_watch_alarm.erb +33 -0
  65. data/lib/terraforming/template/tf/db_parameter_group.erb +17 -0
  66. data/lib/terraforming/template/tf/db_security_group.erb +26 -0
  67. data/lib/terraforming/template/tf/db_subnet_group.erb +8 -0
  68. data/lib/terraforming/template/tf/ec2.erb +56 -0
  69. data/lib/terraforming/template/tf/eip.erb +11 -0
  70. data/lib/terraforming/template/tf/elasti_cache_cluster.erb +22 -0
  71. data/lib/terraforming/template/tf/elasti_cache_subnet_group.erb +8 -0
  72. data/lib/terraforming/template/tf/elastic_filesystem.erb +18 -0
  73. data/lib/terraforming/template/tf/elb.erb +51 -0
  74. data/lib/terraforming/template/tf/iam_group.erb +7 -0
  75. data/lib/terraforming/template/tf/iam_group_membership.erb +8 -0
  76. data/lib/terraforming/template/tf/iam_group_policy.erb +10 -0
  77. data/lib/terraforming/template/tf/iam_instance_profile.erb +8 -0
  78. data/lib/terraforming/template/tf/iam_policy.erb +12 -0
  79. data/lib/terraforming/template/tf/iam_policy_attachment.erb +10 -0
  80. data/lib/terraforming/template/tf/iam_role.erb +10 -0
  81. data/lib/terraforming/template/tf/iam_role_policy.erb +10 -0
  82. data/lib/terraforming/template/tf/iam_user.erb +7 -0
  83. data/lib/terraforming/template/tf/iam_user_policy.erb +10 -0
  84. data/lib/terraforming/template/tf/internet_gateway.erb +14 -0
  85. data/lib/terraforming/template/tf/kms_alias.erb +7 -0
  86. data/lib/terraforming/template/tf/kms_key.erb +13 -0
  87. data/lib/terraforming/template/tf/launch_configuration.erb +68 -0
  88. data/lib/terraforming/template/tf/nat_gateway.erb +9 -0
  89. data/lib/terraforming/template/tf/network_acl.erb +43 -0
  90. data/lib/terraforming/template/tf/network_interface.erb +22 -0
  91. data/lib/terraforming/template/tf/rds.erb +25 -0
  92. data/lib/terraforming/template/tf/redshift.erb +23 -0
  93. data/lib/terraforming/template/tf/route53_record.erb +52 -0
  94. data/lib/terraforming/template/tf/route53_zone.erb +18 -0
  95. data/lib/terraforming/template/tf/route_table.erb +34 -0
  96. data/lib/terraforming/template/tf/route_table_association.erb +9 -0
  97. data/lib/terraforming/template/tf/s3.erb +12 -0
  98. data/lib/terraforming/template/tf/security_group.erb +56 -0
  99. data/lib/terraforming/template/tf/sns_topic.erb +17 -0
  100. data/lib/terraforming/template/tf/sns_topic_subscription.erb +23 -0
  101. data/lib/terraforming/template/tf/sqs.erb +21 -0
  102. data/lib/terraforming/template/tf/subnet.erb +15 -0
  103. data/lib/terraforming/template/tf/vpc.erb +15 -0
  104. data/lib/terraforming/template/tf/vpn_gateway.erb +14 -0
  105. data/lib/terraforming/util.rb +30 -0
  106. data/lib/terraforming/version.rb +3 -0
  107. data/script/console +14 -0
  108. data/script/generate +97 -0
  109. data/script/setup +7 -0
  110. data/templates/resource.erb.erb +3 -0
  111. data/templates/resource.rb.erb +31 -0
  112. data/templates/resource_spec.rb.erb +39 -0
  113. data/terraforming.gemspec +31 -0
  114. metadata +271 -0
@@ -0,0 +1,59 @@
1
+ module Terraforming
2
+ module Resource
3
+ class RouteTableAssociation
4
+ include Terraforming::Util
5
+
6
+ def self.tf(client: Aws::EC2::Client.new)
7
+ self.new(client).tf
8
+ end
9
+
10
+ def self.tfstate(client: Aws::EC2::Client.new)
11
+ self.new(client).tfstate
12
+ end
13
+
14
+ def initialize(client)
15
+ @client = client
16
+ end
17
+
18
+ def tf
19
+ apply_template(@client, "tf/route_table_association")
20
+ end
21
+
22
+ def tfstate
23
+ resources = {}
24
+ route_tables.each do |route_table|
25
+ associations_of(route_table).each do |assoc|
26
+ attributes = {
27
+ "id" => assoc.route_table_association_id,
28
+ "route_table_id" => assoc.route_table_id,
29
+ "subnet_id" => assoc.subnet_id,
30
+ }
31
+
32
+ resources["aws_route_table_association.#{module_name_of(route_table, assoc)}"] = {
33
+ "type" => "aws_route_table_association",
34
+ "primary" => {
35
+ "id" => assoc.route_table_association_id,
36
+ "attributes" => attributes
37
+ }
38
+ }
39
+ end
40
+ end
41
+ resources
42
+ end
43
+
44
+ private
45
+
46
+ def associations_of(route_table)
47
+ route_table.associations.reject { |association| association.subnet_id.nil? }
48
+ end
49
+
50
+ def module_name_of(route_table, assoc)
51
+ normalize_module_name(name_from_tag(route_table, route_table.route_table_id) + '-' + assoc.route_table_association_id)
52
+ end
53
+
54
+ def route_tables
55
+ @client.describe_route_tables.map(&:route_tables).flatten
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,69 @@
1
+ module Terraforming
2
+ module Resource
3
+ class S3
4
+ include Terraforming::Util
5
+
6
+ def self.tf(client: Aws::S3::Client.new)
7
+ self.new(client).tf
8
+ end
9
+
10
+ def self.tfstate(client: Aws::S3::Client.new)
11
+ self.new(client).tfstate
12
+ end
13
+
14
+ def initialize(client)
15
+ @client = client
16
+ end
17
+
18
+ def tf
19
+ apply_template(@client, "tf/s3")
20
+ end
21
+
22
+ def tfstate
23
+ buckets.inject({}) do |resources, bucket|
24
+ bucket_policy = bucket_policy_of(bucket)
25
+ resources["aws_s3_bucket.#{module_name_of(bucket)}"] = {
26
+ "type" => "aws_s3_bucket",
27
+ "primary" => {
28
+ "id" => bucket.name,
29
+ "attributes" => {
30
+ "acl" => "private",
31
+ "bucket" => bucket.name,
32
+ "force_destroy" => "false",
33
+ "id" => bucket.name,
34
+ "policy" => bucket_policy ? bucket_policy.policy.read : "",
35
+ }
36
+ }
37
+ }
38
+
39
+ resources
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def bucket_location_of(bucket)
46
+ @client.get_bucket_location(bucket: bucket.name).location_constraint
47
+ end
48
+
49
+ def bucket_policy_of(bucket)
50
+ @client.get_bucket_policy(bucket: bucket.name)
51
+ rescue Aws::S3::Errors::NoSuchBucketPolicy
52
+ nil
53
+ end
54
+
55
+ def buckets
56
+ @client.list_buckets.map(&:buckets).flatten.select { |bucket| same_region?(bucket) }
57
+ end
58
+
59
+ def module_name_of(bucket)
60
+ normalize_module_name(bucket.name)
61
+ end
62
+
63
+ def same_region?(bucket)
64
+ bucket_location = bucket_location_of(bucket)
65
+ (bucket_location == @client.config.region) || (bucket_location == "" && @client.config.region == "us-east-1")
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,188 @@
1
+ module Terraforming
2
+ module Resource
3
+ class SecurityGroup
4
+ include Terraforming::Util
5
+
6
+ def self.tf(client: Aws::EC2::Client.new)
7
+ self.new(client).tf
8
+ end
9
+
10
+ def self.tfstate(client: Aws::EC2::Client.new)
11
+ self.new(client).tfstate
12
+ end
13
+
14
+ def initialize(client)
15
+ @client = client
16
+ end
17
+
18
+ def tf
19
+ apply_template(@client, "tf/security_group")
20
+ end
21
+
22
+ def tfstate
23
+ security_groups.inject({}) do |resources, security_group|
24
+ attributes = {
25
+ "description" => security_group.description,
26
+ "id" => security_group.group_id,
27
+ "name" => security_group.group_name,
28
+ "owner_id" => security_group.owner_id,
29
+ "vpc_id" => security_group.vpc_id || "",
30
+ }
31
+
32
+ attributes.merge!(tags_attributes_of(security_group))
33
+ attributes.merge!(egress_attributes_of(security_group))
34
+ attributes.merge!(ingress_attributes_of(security_group))
35
+
36
+ resources["aws_security_group.#{module_name_of(security_group)}"] = {
37
+ "type" => "aws_security_group",
38
+ "primary" => {
39
+ "id" => security_group.group_id,
40
+ "attributes" => attributes
41
+ }
42
+ }
43
+
44
+ resources
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def ingress_attributes_of(security_group)
51
+ ingresses = dedup_permissions(security_group.ip_permissions, security_group.group_id)
52
+ attributes = { "ingress.#" => ingresses.length.to_s }
53
+
54
+ ingresses.each do |permission|
55
+ attributes.merge!(permission_attributes_of(security_group, permission, "ingress"))
56
+ end
57
+
58
+ attributes
59
+ end
60
+
61
+ def egress_attributes_of(security_group)
62
+ egresses = dedup_permissions(security_group.ip_permissions_egress, security_group.group_id)
63
+ attributes = { "egress.#" => egresses.length.to_s }
64
+
65
+ egresses.each do |permission|
66
+ attributes.merge!(permission_attributes_of(security_group, permission, "egress"))
67
+ end
68
+
69
+ attributes
70
+ end
71
+
72
+ def group_hashcode_of(group)
73
+ Zlib.crc32(group)
74
+ end
75
+
76
+ def module_name_of(security_group)
77
+ if security_group.vpc_id.nil?
78
+ normalize_module_name(security_group.group_name.to_s)
79
+ else
80
+ normalize_module_name("#{security_group.vpc_id}-#{security_group.group_name}")
81
+ end
82
+ end
83
+
84
+ def permission_attributes_of(security_group, permission, type)
85
+ hashcode = permission_hashcode_of(security_group, permission)
86
+ security_groups = security_groups_in(permission, security_group).reject do |identifier|
87
+ [security_group.group_name, security_group.group_id].include?(identifier)
88
+ end
89
+
90
+ attributes = {
91
+ "#{type}.#{hashcode}.from_port" => (permission.from_port || 0).to_s,
92
+ "#{type}.#{hashcode}.to_port" => (permission.to_port || 0).to_s,
93
+ "#{type}.#{hashcode}.protocol" => permission.ip_protocol,
94
+ "#{type}.#{hashcode}.cidr_blocks.#" => permission.ip_ranges.length.to_s,
95
+ "#{type}.#{hashcode}.prefix_list_ids.#" => permission.prefix_list_ids.length.to_s,
96
+ "#{type}.#{hashcode}.security_groups.#" => security_groups.length.to_s,
97
+ "#{type}.#{hashcode}.self" => self_referenced_permission?(security_group, permission).to_s,
98
+ }
99
+
100
+ permission.ip_ranges.each_with_index do |range, index|
101
+ attributes["#{type}.#{hashcode}.cidr_blocks.#{index}"] = range.cidr_ip
102
+ end
103
+
104
+ permission.prefix_list_ids.each_with_index do |prefix_list, index|
105
+ attributes["#{type}.#{hashcode}.prefix_list_ids.#{index}"] = prefix_list.prefix_list_id
106
+ end
107
+
108
+ security_groups.each do |group|
109
+ attributes["#{type}.#{hashcode}.security_groups.#{group_hashcode_of(group)}"] = group
110
+ end
111
+
112
+ attributes
113
+ end
114
+
115
+ def dedup_permissions(permissions, group_id)
116
+ group_permissions(permissions).inject([]) do |result, (_, perms)|
117
+ group_ids = perms.map(&:user_id_group_pairs).flatten.map(&:group_id)
118
+
119
+ if group_ids.length == 1 && group_ids.first == group_id
120
+ result << merge_permissions(perms)
121
+ else
122
+ result.concat(perms)
123
+ end
124
+
125
+ result
126
+ end
127
+ end
128
+
129
+ def group_permissions(permissions)
130
+ permissions.group_by { |permission| [permission.ip_protocol, permission.to_port, permission.from_port] }
131
+ end
132
+
133
+ def merge_permissions(permissions)
134
+ master_permission = permissions.pop
135
+
136
+ permissions.each do |permission|
137
+ master_permission.user_id_group_pairs.concat(permission.user_id_group_pairs)
138
+ master_permission.ip_ranges.concat(permission.ip_ranges)
139
+ end
140
+
141
+ master_permission
142
+ end
143
+
144
+ def permission_hashcode_of(security_group, permission)
145
+ string =
146
+ "#{permission.from_port || 0}-" <<
147
+ "#{permission.to_port || 0}-" <<
148
+ "#{permission.ip_protocol}-" <<
149
+ "#{self_referenced_permission?(security_group, permission)}-"
150
+
151
+ permission.ip_ranges.each { |range| string << "#{range.cidr_ip}-" }
152
+ security_groups_in(permission, security_group).each { |group| string << "#{group}-" }
153
+
154
+ Zlib.crc32(string)
155
+ end
156
+
157
+ def self_referenced_permission?(security_group, permission)
158
+ (security_groups_in(permission, security_group) & [security_group.group_id, security_group.group_name]).any?
159
+ end
160
+
161
+ def security_groups
162
+ @client.describe_security_groups.map(&:security_groups).flatten
163
+ end
164
+
165
+ def security_groups_in(permission, security_group)
166
+ permission.user_id_group_pairs.map do |range|
167
+ # EC2-Classic, same account
168
+ if security_group.owner_id == range.user_id && !range.group_name.nil?
169
+ range.group_name
170
+ # VPC
171
+ elsif security_group.owner_id == range.user_id && range.group_name.nil?
172
+ range.group_id
173
+ # EC2-Classic, other account
174
+ else
175
+ "#{range.user_id}/#{range.group_name}"
176
+ end
177
+ end
178
+ end
179
+
180
+ def tags_attributes_of(security_group)
181
+ tags = security_group.tags
182
+ attributes = { "tags.#" => tags.length.to_s }
183
+ tags.each { |tag| attributes["tags.#{tag.key}"] = tag.value }
184
+ attributes
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,75 @@
1
+ module Terraforming
2
+ module Resource
3
+ class SNSTopic
4
+ include Terraforming::Util
5
+
6
+ def self.tf(client: Aws::SNS::Client.new)
7
+ self.new(client).tf
8
+ end
9
+
10
+ def self.tfstate(client: Aws::SNS::Client.new)
11
+ self.new(client).tfstate
12
+ end
13
+
14
+ def initialize(client)
15
+ @client = client
16
+ end
17
+
18
+ def tf
19
+ apply_template(@client, "tf/sns_topic")
20
+ end
21
+
22
+ def tfstate
23
+ topics.inject({}) do |resources, topic|
24
+ attributes = {
25
+ "name" => module_name_of(topic),
26
+ "id" => topic["TopicArn"],
27
+ "arn" => topic["TopicArn"],
28
+ "display_name" => topic["DisplayName"],
29
+ "policy" => topic.key?("Policy") ? topic["Policy"] : "",
30
+ "delivery_policy" => topic.key?("DeliveryPolicy") ? topic["DeliveryPolicy"] : ""
31
+ }
32
+ resources["aws_sns_topic.#{module_name_of(topic)}"] = {
33
+ "type" => "aws_sns_topic",
34
+ "primary" => {
35
+ "id" => topic["TopicArn"],
36
+ "attributes" => attributes
37
+ }
38
+ }
39
+
40
+ resources
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def topics
47
+ topic_arns.map do |topic_arn|
48
+ attributes = @client.get_topic_attributes({
49
+ topic_arn: topic_arn,
50
+ }).attributes
51
+ attributes["TopicArn"] = topic_arn
52
+ attributes
53
+ end
54
+ end
55
+
56
+ def topic_arns
57
+ token = ""
58
+ arns = []
59
+
60
+ loop do
61
+ resp = @client.list_topics(next_token: token)
62
+ arns += resp.topics.map(&:topic_arn).flatten
63
+ token = resp.next_token
64
+ break if token.nil?
65
+ end
66
+
67
+ arns
68
+ end
69
+
70
+ def module_name_of(topic)
71
+ normalize_module_name(topic["TopicArn"].split(":").last)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,83 @@
1
+ module Terraforming
2
+ module Resource
3
+ class SNSTopicSubscription
4
+ include Terraforming::Util
5
+
6
+ def self.tf(client: Aws::SNS::Client.new)
7
+ self.new(client).tf
8
+ end
9
+
10
+ def self.tfstate(client: Aws::SNS::Client.new)
11
+ self.new(client).tfstate
12
+ end
13
+
14
+ def initialize(client)
15
+ @client = client
16
+ end
17
+
18
+ def tf
19
+ apply_template(@client, "tf/sns_topic_subscription")
20
+ end
21
+
22
+ def tfstate
23
+ subscriptions.reject { |x| x["Protocol"].include?("email") }
24
+ .inject({}) do |resources, subscription|
25
+ attributes = {
26
+ "id" => subscription["SubscriptionArn"],
27
+ "topic_arn" => subscription["TopicArn"],
28
+ "protocol" => subscription["Protocol"],
29
+ "endpoint" => subscription["Endpoint"],
30
+ "raw_message_delivery" =>
31
+ subscription.key?("RawMessageDelivery") ? subscription["RawMessageDelivery"] : "false",
32
+ "confirmation_timeout_in_minutes" =>
33
+ subscription.key?("ConfirmationTimeoutInMinutes") ? subscription["ConfirmationTimeoutInMinutes"] : "1",
34
+ "endpoint_auto_confirms" =>
35
+ subscription.key?("EndpointAutoConfirms") ? subscription["EndpointAutoConfirms"] : "false"
36
+ }
37
+ resources["aws_sns_topic_subscription.#{module_name_of(subscription)}"] = {
38
+ "type" => "aws_sns_topic_subscription",
39
+ "primary" => {
40
+ "id" => subscription["SubscriptionArn"],
41
+ "attributes" => attributes
42
+ }
43
+ }
44
+
45
+ resources
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def subscriptions
52
+ subscription_arns.map do |subscription_arn|
53
+ # check explicitly for an issue with some subscriptions that returns ARN=PendingConfirmation
54
+ next if subscription_arn == "PendingConfirmation"
55
+
56
+ attributes = @client.get_subscription_attributes({
57
+ subscription_arn: subscription_arn,
58
+ }).attributes
59
+ attributes["SubscriptionArn"] = subscription_arn
60
+ attributes
61
+ end.compact
62
+ end
63
+
64
+ def subscription_arns
65
+ token = ""
66
+ arns = []
67
+
68
+ loop do
69
+ resp = @client.list_subscriptions(next_token: token)
70
+ arns += resp.subscriptions.map(&:subscription_arn).flatten
71
+ token = resp.next_token
72
+ break if token.nil?
73
+ end
74
+
75
+ arns
76
+ end
77
+
78
+ def module_name_of(subscription)
79
+ normalize_module_name(subscription["SubscriptionArn"].split(":").last)
80
+ end
81
+ end
82
+ end
83
+ end