convection 2.2.12 → 2.2.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/convection +38 -15
- data/lib/convection/model/template.rb +16 -0
- data/lib/convection/model/template/resource/aws_ec2_security_group.rb +52 -0
- data/lib/convection/model/template/resource/aws_iam_user.rb +55 -0
- data/lib/convection/model/template/resource/aws_s3_bucket.rb +24 -0
- data/lib/convection/model/template/resource/aws_s3_bucket_policy.rb +22 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc4c4bbe225ec33757f900d24c62f5629644e4ae
|
4
|
+
data.tar.gz: b3e00e9748782113a6a7491cacb12cdc0b2f71e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 731c78af0486d452e249a27c40499bbe4d44d44df7a5fd35c7501c6104ddb4720ccdf60b870574f2b3808510a85870530351e241a20aa6167fe712116a21d09f
|
7
|
+
data.tar.gz: 88af87c51fa62ca1be9e73748d90abfe9a0bbb63b0946d028fe3a5a2ae01eb218b6285ae4145faf4fb40acac07cf2cfb864697dad6678a7e156679a6529bc653
|
data/bin/convection
CHANGED
@@ -130,22 +130,19 @@ module Convection
|
|
130
130
|
m['resources'].reject { |k, _| k.start_with?('data.') }
|
131
131
|
}.reduce({}, &:merge)
|
132
132
|
cf = Aws::CloudFormation::Client.new(region: options[:stack_region])
|
133
|
-
cf_resources = cf.describe_stack_resources(stack_name: options[:stack_name]).stack_resources
|
133
|
+
cf_resources = cf.describe_stack_resources(stack_name: options[:stack_name]).stack_resources.map do |r|
|
134
|
+
{ logical_resource_id: r.logical_resource_id, physical_resource_id: r.physical_resource_id, type: r.resource_type }
|
135
|
+
end
|
134
136
|
cf_resources.each do |cf_resource|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
tf_key ||= tf_resource && tf_resource['primary'].find { |v| v.include?(cf_resource.physical_resource_id) }
|
145
|
-
if tf_key && fuzzy_match
|
146
|
-
say " * Found physical ID included in Terraform resource #{tf_resource_name} under attribute #{tf_key} with value '#{tf_resource['primary'][tf_key]}'. Please validate this resource is included in state and configuration.", :yellow
|
147
|
-
elsif tf_key && !fuzzy_match
|
148
|
-
say " * Found physical ID exactly in Terraform resource #{tf_resource_name} under attribute #{tf_key} with value '#{tf_resource['primary'][tf_key]}'.", :cyan
|
137
|
+
response = ask_tf_physical_id(cf_resource)
|
138
|
+
cf_resource[:physical_resource_id] = response unless response.empty?
|
139
|
+
say "Resource #{cf_resource[:physical_resource_id].inspect} #{cf_resource[:logical_resource_id].inspect}:"
|
140
|
+
|
141
|
+
tf_resource = tf_resource_for(physical_resource_id: cf_resource[:physical_resource_id], resources: terraform_resources)
|
142
|
+
if tf_resource[:attribute_key] && tf_resource[:partial]
|
143
|
+
say " * Found physical ID included in Terraform resource #{tf_resource[:logical_resource_id].inspect} under attribute #{tf_resource[:attribute_key].inspect} with value #{tf_resource[:value].inspect}. Please validate this resource is included in state and configuration.", :yellow
|
144
|
+
elsif tf_resource[:attribute_key] && !tf_resource[:partial]
|
145
|
+
say " * Found physical ID exactly in Terraform resource #{tf_resource[:logical_resource_id].inspect} under attribute #{tf_resource[:attribute_key].inspect} with value #{tf_resource[:value].inspect}.", :green
|
149
146
|
else
|
150
147
|
say " * Unable to find physical ID in the provided Terraform state.", :red
|
151
148
|
end
|
@@ -280,6 +277,18 @@ module Convection
|
|
280
277
|
|
281
278
|
private
|
282
279
|
|
280
|
+
def ask_tf_physical_id(cf_resource)
|
281
|
+
question = case cf_resource[:type]
|
282
|
+
when 'AWS::IAM::Policy'
|
283
|
+
"What is the <role-name>:<policy-name> for the #{cf_resource[:logical_resource_id].inspect} IAM policy?"
|
284
|
+
when 'AWS::S3::BucketPolicy'
|
285
|
+
"What is the name of the bucket that the #{cf_resource[:logical_resource_id].inspect} policy belongs to?"
|
286
|
+
end
|
287
|
+
return '' unless question
|
288
|
+
|
289
|
+
ask(question, :yellow).chomp
|
290
|
+
end
|
291
|
+
|
283
292
|
def load_local_terraform_state(statefile)
|
284
293
|
JSON.load(File.open(statefile))
|
285
294
|
end
|
@@ -306,6 +315,20 @@ module Convection
|
|
306
315
|
end
|
307
316
|
end
|
308
317
|
|
318
|
+
def tf_resource_for(physical_resource_id:, resources:)
|
319
|
+
logical_id, resource = resources.find do |_, resource|
|
320
|
+
next true if resource['primary'].key(physical_resource_id)
|
321
|
+
resource['primary'].values.any? { |v| v.is_a?(String) && v.include?(physical_resource_id) }
|
322
|
+
end
|
323
|
+
return {} unless logical_id && resource
|
324
|
+
|
325
|
+
key = resource['primary'].key(physical_resource_id)
|
326
|
+
return { attribute_key: key, logical_id: logical_id, partial: false, value: physical_resource_id } if key
|
327
|
+
|
328
|
+
key, value = value['primary'].find { |v| v.include?(physical_resource_id) }
|
329
|
+
return { attribute_key: key, logical_id: logical_id, partial: true, value: value }
|
330
|
+
end
|
331
|
+
|
309
332
|
def import_resources(resource_name, resource)
|
310
333
|
empty_directory options[:output_directory] if resource.respond_to?(:to_hcl_json) || resource.respond_to?(:additional_hcl_files)
|
311
334
|
if resource.respond_to?(:to_hcl_json)
|
@@ -41,6 +41,22 @@ module Convection
|
|
41
41
|
@resource_collection_dsl_methods ||= {}
|
42
42
|
end
|
43
43
|
end
|
44
|
+
|
45
|
+
def _terraform_module_dir_to_flag(dir)
|
46
|
+
return 'root' if dir.empty?
|
47
|
+
|
48
|
+
parts = dir.split('/')
|
49
|
+
parts[0] = 'module' if parts[0] == 'modules'
|
50
|
+
parts.join('.')
|
51
|
+
end
|
52
|
+
|
53
|
+
def _terraform_module_flag_to_dir(flag)
|
54
|
+
return '' if flag == 'root'
|
55
|
+
|
56
|
+
parts = flag.split('.')
|
57
|
+
parts[0] = 'modules'
|
58
|
+
parts.join('/')
|
59
|
+
end
|
44
60
|
end
|
45
61
|
|
46
62
|
include DSL::Helpers
|
@@ -116,6 +116,58 @@ module Convection
|
|
116
116
|
render_tags(resource)
|
117
117
|
end
|
118
118
|
end
|
119
|
+
|
120
|
+
def to_hcl_json(*)
|
121
|
+
tf_sg_name = name.underscore
|
122
|
+
tf_sg_var_id = "${aws_security_group.#{tf_sg_name}.id}"
|
123
|
+
tf_resources = []
|
124
|
+
|
125
|
+
# Define the security group resource.
|
126
|
+
tf_resources << {
|
127
|
+
aws_security_group: {
|
128
|
+
tf_sg_name => {
|
129
|
+
vpc_id: vpc,
|
130
|
+
description: description,
|
131
|
+
tags: tags.reject { |_, v| v.nil? }
|
132
|
+
}.reject { |_, v| v.nil? }
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
tf_sg_rules = {}
|
137
|
+
|
138
|
+
# Define helper functions to map Convection rules to Terraform ones.
|
139
|
+
sg_rule_to_tf = lambda do |rule_type, item, index|
|
140
|
+
tf_sg_rule_name = "#{tf_sg_name}_#{rule_type}_#{index}"
|
141
|
+
|
142
|
+
tf_sg_rules[tf_sg_rule_name] = {
|
143
|
+
type: rule_type,
|
144
|
+
security_group_id: tf_sg_var_id,
|
145
|
+
from_port: item.from,
|
146
|
+
to_port: item.to,
|
147
|
+
protocol: item.protocol,
|
148
|
+
cidr_block: item.source,
|
149
|
+
# TODO: Missing attribs & checks. Should probably be defined as a
|
150
|
+
# seperate function to reuse for egress.
|
151
|
+
}.reject { |_, v| v.nil? }
|
152
|
+
end
|
153
|
+
|
154
|
+
# Map the contained rules to TF.
|
155
|
+
security_group_ingress.each_with_index { |item, obj| sg_rule_to_tf.call('ingress', item, obj) }
|
156
|
+
security_group_egress.each_with_index { |item, obj| sg_rule_to_tf.call('egress', item, obj) }
|
157
|
+
|
158
|
+
tf_resources << { aws_security_group_rule: tf_sg_rules }
|
159
|
+
|
160
|
+
# Return the JSON representation of this resource.
|
161
|
+
{ resource: tf_resources }.to_json
|
162
|
+
end
|
163
|
+
|
164
|
+
def terraform_import_commands(module_path: 'root')
|
165
|
+
prefix = "#{module_path}." unless module_path == 'root'
|
166
|
+
resource_id = stack.resources[name] && stack.resources[name].physical_resource_id
|
167
|
+
commands = ['# Import the security group:']
|
168
|
+
commands << "terraform import #{prefix}aws_security_group.#{name.underscore} #{resource_id}"
|
169
|
+
commands
|
170
|
+
end
|
119
171
|
end
|
120
172
|
end
|
121
173
|
end
|
@@ -63,6 +63,61 @@ module Convection
|
|
63
63
|
property :path, 'Path'
|
64
64
|
property :policies, 'Policies', :type => :list
|
65
65
|
property :user_name, 'UserName'
|
66
|
+
|
67
|
+
def additional_hcl_files(module_path: 'root')
|
68
|
+
module_prefix = module_path.tr('.', '-') if module_path == 'root'
|
69
|
+
result = {}
|
70
|
+
|
71
|
+
user = user_name
|
72
|
+
user ||= stack.resources[name] && stack.resources[name].physical_resource_id
|
73
|
+
result["#{stack._original_region}-#{stack._original_cloud}-#{name.underscore}.tf.json"] = {
|
74
|
+
module: [{
|
75
|
+
name.underscore => {
|
76
|
+
source: _terraform_module_flag_to_dir(module_path),
|
77
|
+
managed_policy_arns: managed_policy_arn,
|
78
|
+
name: user,
|
79
|
+
path: path
|
80
|
+
}
|
81
|
+
}]
|
82
|
+
}
|
83
|
+
|
84
|
+
result["#{module_prefix}#{name.underscore}-variables.tf.json"] = {
|
85
|
+
variable: [
|
86
|
+
{ managed_policy_arns: { description: 'A list of ARNs for managed policies to attach to this user.', default: [] } },
|
87
|
+
{ name: { description: 'The name of the user' } },
|
88
|
+
{ path: { description: 'The path for the IAM user', path: '/' } }
|
89
|
+
]
|
90
|
+
}
|
91
|
+
|
92
|
+
result["#{module_prefix}#{name.underscore}-user.tf.json"] = {
|
93
|
+
resource: [
|
94
|
+
{
|
95
|
+
aws_iam_user: {
|
96
|
+
name.underscore => {
|
97
|
+
managed_policy_arns: '${var.managed_policy_arns}',
|
98
|
+
name: '${var.name}',
|
99
|
+
path: '${var.path}'
|
100
|
+
}
|
101
|
+
}
|
102
|
+
}
|
103
|
+
]
|
104
|
+
}
|
105
|
+
|
106
|
+
policy_resources = policies.map do |policy|
|
107
|
+
{
|
108
|
+
aws_iam_user_policy: {
|
109
|
+
policy.name.underscore => {
|
110
|
+
name: policy.name,
|
111
|
+
policy: policy.render.to_json,
|
112
|
+
user: "${aws_iam_user.#{name.underscore}.id}"
|
113
|
+
}
|
114
|
+
}
|
115
|
+
}
|
116
|
+
end
|
117
|
+
result["#{module_prefix}#{name.underscore}-policy.tf.json"] = { resource: policy_resources }
|
118
|
+
|
119
|
+
result
|
120
|
+
end
|
66
121
|
end
|
67
122
|
end
|
68
123
|
end
|
@@ -50,6 +50,30 @@ module Convection
|
|
50
50
|
properties['WebsiteConfiguration'].set(config)
|
51
51
|
end
|
52
52
|
|
53
|
+
def terraform_import_commands(module_path: 'root')
|
54
|
+
commands = ['# Run the following commands to import your infrastructure into terraform management.', '# ensure :module_path is set correctly', '']
|
55
|
+
module_prefix = "#{module_path}." unless module_path == 'root'
|
56
|
+
|
57
|
+
commands << '# Import s3 bucket and s3 bucket policy: '
|
58
|
+
commands << "terraform import #{module_prefix}aws_s3_bucket.#{name.underscore} #{stack.resources[name].physical_resource_id}"
|
59
|
+
commands << ''
|
60
|
+
commands
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_hcl_json(*)
|
64
|
+
bucket_resource = {
|
65
|
+
name.underscore => {
|
66
|
+
bucket: stack.resources[name].physical_resource_id,
|
67
|
+
acl: 'private',
|
68
|
+
force_destroy: false
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
data = [{ aws_region: { current: { current: true } } }]
|
73
|
+
vars = [{ cloud: { description: 'The cloud name for this resource.' } }]
|
74
|
+
{ resource: [{ aws_s3_bucket: bucket_resource }], data: data, variable: vars }.to_json
|
75
|
+
end
|
76
|
+
|
53
77
|
def render(*args)
|
54
78
|
super.tap do |resource|
|
55
79
|
render_tags(resource)
|
@@ -30,6 +30,28 @@ module Convection
|
|
30
30
|
@document = Model::Mixin::Policy.new(:name => false, :template => @template)
|
31
31
|
end
|
32
32
|
|
33
|
+
def terraform_import_commands(*)
|
34
|
+
commands = ['# Run the following commands to import your infrastructure into terraform management.', '# ensure :module_path is set correctly', '']
|
35
|
+
commands << '# Import s3 bucket and s3 bucket policy: '
|
36
|
+
# commands << "terraform import #{module_prefix}aws_s3_bucket.#{name.underscore} #{stack.resources[name].physical_resource_id}"
|
37
|
+
commands << ''
|
38
|
+
commands
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_hcl_json(*)
|
42
|
+
policy_json = resources[name] && resources[name].document.document.to_json.gsub(stack.resources[name].physical_resource_id, bucket)
|
43
|
+
policy_resource = {
|
44
|
+
name.underscore => {
|
45
|
+
bucket: bucket,
|
46
|
+
policy: policy_json
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
data = [{ aws_region: { current: { current: true } } }]
|
51
|
+
vars = [{ cloud: { description: 'The cloud name for this resource.' } }]
|
52
|
+
{ resource: { aws_s3_bucket_policy: policy_resource }, data: data, variable: vars }.to_json
|
53
|
+
end
|
54
|
+
|
33
55
|
def render
|
34
56
|
super.tap do |r|
|
35
57
|
document.render(r['Properties'])
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: convection
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2.
|
4
|
+
version: 2.2.13
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Manero
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-01-
|
11
|
+
date: 2018-01-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|