enscalator 0.4.0.pre.alpha.pre.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rubocop.yml +9 -0
  4. data/.rubocop_todo.yml +59 -0
  5. data/.travis.yml +22 -0
  6. data/CODE_OF_CONDUCT.md +13 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +148 -0
  10. data/Rakefile +43 -0
  11. data/bin/console +11 -0
  12. data/bin/setup +7 -0
  13. data/enscalator.gemspec +57 -0
  14. data/exe/enscalator +13 -0
  15. data/lib/enscalator/core/cf_parameters.rb +146 -0
  16. data/lib/enscalator/core/cf_resources.rb +225 -0
  17. data/lib/enscalator/core/instance_type.rb +205 -0
  18. data/lib/enscalator/core/network_config.rb +21 -0
  19. data/lib/enscalator/core.rb +10 -0
  20. data/lib/enscalator/enapp.rb +248 -0
  21. data/lib/enscalator/helpers/dns.rb +62 -0
  22. data/lib/enscalator/helpers/stack.rb +107 -0
  23. data/lib/enscalator/helpers/sub_process.rb +72 -0
  24. data/lib/enscalator/helpers/wrappers.rb +55 -0
  25. data/lib/enscalator/helpers.rb +127 -0
  26. data/lib/enscalator/plugins/amazon_linux.rb +93 -0
  27. data/lib/enscalator/plugins/auto_scale.rb +80 -0
  28. data/lib/enscalator/plugins/core_os.rb +88 -0
  29. data/lib/enscalator/plugins/couchbase.rb +98 -0
  30. data/lib/enscalator/plugins/debian.rb +71 -0
  31. data/lib/enscalator/plugins/elastic_beanstalk.rb +74 -0
  32. data/lib/enscalator/plugins/elasticache.rb +168 -0
  33. data/lib/enscalator/plugins/elasticsearch_amazon.rb +75 -0
  34. data/lib/enscalator/plugins/elasticsearch_bitnami.rb +198 -0
  35. data/lib/enscalator/plugins/elasticsearch_opsworks.rb +225 -0
  36. data/lib/enscalator/plugins/elb.rb +139 -0
  37. data/lib/enscalator/plugins/nat_gateway.rb +71 -0
  38. data/lib/enscalator/plugins/rds.rb +141 -0
  39. data/lib/enscalator/plugins/redis.rb +38 -0
  40. data/lib/enscalator/plugins/rethink_db.rb +21 -0
  41. data/lib/enscalator/plugins/route53.rb +143 -0
  42. data/lib/enscalator/plugins/ubuntu.rb +85 -0
  43. data/lib/enscalator/plugins/user-data/elasticsearch +367 -0
  44. data/lib/enscalator/plugins/vpc_peering_connection.rb +48 -0
  45. data/lib/enscalator/plugins.rb +30 -0
  46. data/lib/enscalator/rich_template_dsl.rb +209 -0
  47. data/lib/enscalator/templates/vpc_peering.rb +112 -0
  48. data/lib/enscalator/templates.rb +20 -0
  49. data/lib/enscalator/version.rb +5 -0
  50. data/lib/enscalator/vpc.rb +11 -0
  51. data/lib/enscalator/vpc_with_nat_gateway.rb +311 -0
  52. data/lib/enscalator/vpc_with_nat_instance.rb +402 -0
  53. data/lib/enscalator.rb +103 -0
  54. metadata +427 -0
@@ -0,0 +1,75 @@
1
+ module Enscalator
2
+ module Plugins
3
+ # Amazon Elasticsearch Service (Amazon ES)
4
+ module ElasticsearchAmazon
5
+ # Create new service instance
6
+ # @param [String] cluster_name name of the cluster resource
7
+ # @param [Hash] properties additional parameters for cluster configuration
8
+ def elasticsearch_init(cluster_name, properties: {})
9
+ cluster_properties = {
10
+ AccessPolicies: {
11
+ Version: '2012-10-17',
12
+ Statement: [
13
+ {
14
+ Effect: 'Allow',
15
+ Principal: {
16
+ AWS: '*'
17
+ }
18
+ }
19
+ ]
20
+ },
21
+ AdvancedOptions: {
22
+ 'rest.action.multi.allow_explicit_index': 'true'
23
+ },
24
+ EBSOptions: {
25
+ EBSEnabled: true,
26
+ Iops: 0,
27
+ VolumeSize: 100,
28
+ VolumeType: 'gp2'
29
+ },
30
+ ElasticsearchClusterConfig: {
31
+ InstanceCount: '1',
32
+ InstanceType: 'm3.medium.elasticsearch'
33
+ },
34
+ SnapshotOptions: {
35
+ AutomatedSnapshotStartHour: '0'
36
+ }
37
+ }
38
+
39
+ # do not modify properties passed from template
40
+ props = properties.deep_dup
41
+
42
+ default_tags = [
43
+ {
44
+ Key: 'ClusterName',
45
+ Value: cluster_name.downcase
46
+ }
47
+ ]
48
+
49
+ if props.key?(:Tags) && !props[:Tags].empty?
50
+ props[:Tags].concat(default_tags)
51
+ else
52
+ props[:Tags] = default_tags
53
+ end
54
+
55
+ resource cluster_name,
56
+ Type: 'AWS::Elasticsearch::Domain',
57
+ Properties: cluster_properties.merge(props)
58
+
59
+ output "#{cluster_name}ResourceID",
60
+ Description: "#{cluster_name} ResourceID",
61
+ Value: ref(cluster_name)
62
+
63
+ output "#{cluster_name}DomainArn",
64
+ Description: "#{cluster_name} DomainArn",
65
+ Value: get_att(cluster_name, 'DomainArn')
66
+
67
+ output "#{cluster_name}DomainEndpoint",
68
+ Description: "#{cluster_name} DomainEndpoint",
69
+ Value: get_att(cluster_name, 'DomainEndpoint')
70
+
71
+ cluster_name
72
+ end
73
+ end # module ElasticsearchAmazon
74
+ end # module Plugins
75
+ end # module Enscalator
@@ -0,0 +1,198 @@
1
+ module Enscalator
2
+ module Plugins
3
+ # Elasticsearch related configuration
4
+ module ElasticsearchBitnami
5
+ # Retrieves mapping for Elasticsearch Bitnami stack
6
+ class << self
7
+ # Supported storage types in AWS
8
+ STORAGE = [:ebs, :'instance-store']
9
+
10
+ # Supported Elasticsearch image architectures
11
+ ARCH = [:amd64, :i386]
12
+
13
+ # Get ami region/virtualization type mapping
14
+ #
15
+ # @param [Symbol] storage image root storage
16
+ # @param [Symbol] arch image architecture type
17
+ # @return [Hash] mapping
18
+ def get_mapping(storage: :ebs, arch: :amd64)
19
+ fail ArgumentError, "storage can only be one of #{STORAGE}" unless STORAGE.include? storage
20
+ fail ArgumentError, "arch can only be one of #{ARCH}" unless ARCH.include? arch
21
+ fetch_mapping(storage, arch)
22
+ end
23
+
24
+ # Get ami release version string
25
+ #
26
+ # @param [Symbol] storage image root storage
27
+ # @param [Symbol] arch image architecture type
28
+ # @return [Hash] mapping
29
+ def get_release_version(storage: :ebs, arch: :amd64)
30
+ fail ArgumentError, "storage can only be one of #{STORAGE}" unless STORAGE.include? storage
31
+ fail ArgumentError, "arch can only be one of #{ARCH}" unless ARCH.include? arch
32
+ fetch_versions
33
+ .select { |r| r.root_storage == storage && r.arch == arch }
34
+ .map { |v| v.version.to_s }.uniq.first
35
+ .gsub(/[-][\w\d]/, '')
36
+ end
37
+
38
+ private
39
+
40
+ # Structure to hold parsed record
41
+ Struct.new('ElasticSearch', :name, :version, :baseos, :root_storage, :arch, :region, :ami, :virtualization)
42
+
43
+ # Always fetches the most recent version
44
+ #
45
+ # @param [Symbol] storage image root storage
46
+ # @param [Symbol] arch image architecture type
47
+ # @return [Hash] mapping
48
+ def fetch_mapping(storage, arch)
49
+ versions = fetch_versions
50
+ versions.select { |r| r.root_storage == storage && r.arch == arch }
51
+ .group_by(&:region)
52
+ .map { |k, v| [k, v.map { |i| [i.virtualization, i.ami] }.to_h] }.to_h
53
+ .with_indifferent_access
54
+ end
55
+
56
+ # Make request to Bitnami Elasticsearch release pages, parse response and make list of versions
57
+ #
58
+ # @return [Array] list of all versions across all AWS regions
59
+ def fetch_versions
60
+ html = Nokogiri::HTML(open('https://bitnami.com/stack/elasticsearch/cloud/amazon'))
61
+ raw_entries = html.xpath('//td[@class="instance_id"]')
62
+ entries = raw_entries.xpath('a')
63
+ raw_entries.xpath('strong/a').each { |sa| entries << sa }
64
+ raw_versions = entries.map do |i|
65
+ [
66
+ i.xpath('@href').first.value.split('/').last,
67
+ i.children.first.text
68
+ ]
69
+ end.to_h
70
+ parse_versions(raw_versions)
71
+ end
72
+
73
+ # Parse list of raw strings
74
+ #
75
+ # @param entries [Array] list of strings
76
+ # @return [Array]
77
+ def parse_versions(entries)
78
+ entries.map do |rw, ami|
79
+ str, region = rw.split('?').map { |s| s.start_with?('region') ? s.split('=').last : s }
80
+ version_str = fix_entry(str).split('-')
81
+ name, version, baseos = version_str
82
+ Struct::ElasticSearch.new(name,
83
+ Semantic::Version.new(version.tr('=', '-')),
84
+ baseos,
85
+ version_str.include?('ebs') ? :ebs : :'instance-store',
86
+ version_str.include?('x64') ? :amd64 : :i386,
87
+ region,
88
+ ami,
89
+ version_str.include?('hvm') ? :hvm : :pv)
90
+ end
91
+ end
92
+
93
+ # Fix elasticsearch version string to have predictable format
94
+ #
95
+ # @param [String] str raw version string
96
+ # @return [String] reformatted version string
97
+ def fix_entry(str)
98
+ pattern = '[-](?:[\w\d]+){1,3}[-]ami'
99
+ token = begin
100
+ Regexp.new(pattern.gsub('?:', '')).match(str)[1]
101
+ rescue
102
+ nil
103
+ end
104
+ str.gsub(Regexp.new(pattern), ['=', token, '-'].join)
105
+ end
106
+ end # class << self
107
+
108
+ # Get aws account id
109
+ # @return [String] account id
110
+ def aws_account_id
111
+ # noinspection RubyArgCount
112
+ Aws::IAM::Client.new.get_user.user.arn.split(':')[4]
113
+ end
114
+
115
+ # Create new elasticsearch instance
116
+ #
117
+ # @param [String] storage_name storage name
118
+ # @param [Integer] allocated_storage size of instance primary storage
119
+ # @param [String] instance_type instance type
120
+ # @param [Hash] properties additional properties
121
+ # @param [String] zone_name route53 zone name
122
+ def elasticsearch_init(storage_name,
123
+ allocated_storage: 5,
124
+ instance_type: 't2.medium',
125
+ properties: {},
126
+ zone_name: nil)
127
+
128
+ @es_key_name = gen_ssh_key_name("Elasticsearch#{storage_name}", region, stack_name)
129
+ pre_run { create_ssh_key(@es_key_name, region, force_create: false) }
130
+
131
+ mapping 'AWSElasticsearchAMI', ElasticsearchBitnami.get_mapping
132
+
133
+ parameter_allocated_storage "Elasticsearch#{storage_name}",
134
+ default: allocated_storage,
135
+ min: 5,
136
+ max: 1024
137
+
138
+ parameter_ec2_instance_type "Elasticsearch#{storage_name}", type: instance_type
139
+
140
+ properties[:KeyName] = @es_key_name
141
+ properties[:InstanceType] = ref("Elasticsearch#{storage_name}InstanceType")
142
+
143
+ version_tag = {
144
+ Key: 'Version',
145
+ Value: ElasticsearchBitnami.get_release_version
146
+ }
147
+
148
+ cluster_name_tag = {
149
+ Key: 'ClusterName',
150
+ Value: storage_name.downcase
151
+ }
152
+
153
+ plugin_tags = [version_tag, cluster_name_tag]
154
+
155
+ # Set instance tags
156
+ if properties.key?(:Tags) && !properties[:Tags].empty?
157
+ properties[:Tags].concat(plugin_tags)
158
+ else
159
+ properties[:Tags] = plugin_tags
160
+ end
161
+
162
+ # Configure instance using user-data
163
+ if !properties.key?(:UserData) || !properties[:UserData].empty?
164
+ properties[:UserData] = Base64.encode64(read_user_data('elasticsearch'))
165
+ end
166
+
167
+ # Assign IAM role to instance
168
+ properties[:IamInstanceProfile] = iam_instance_profile_with_full_access(storage_name, *%w(ec2 s3))
169
+
170
+ storage_resource_name = "Elasticsearch#{storage_name}"
171
+ instance_vpc storage_resource_name,
172
+ find_in_map('AWSElasticsearchAMI', ref('AWS::Region'), :hvm),
173
+ ref_application_subnets.first,
174
+ [ref_private_security_group, ref_resource_security_group],
175
+ depends_on: [],
176
+ properties: properties
177
+
178
+ # create s3 bucket for cluster snapshots
179
+ account_id = aws_account_id
180
+ bucket_name = "elasticsearch-bitnami-#{region}-#{account_id}"
181
+ resource "Elasticsearch#{storage_name}S3Bucket",
182
+ Type: 'AWS::S3::Bucket',
183
+ DeletionPolicy: 'Retain',
184
+ Properties: {
185
+ BucketName: bucket_name
186
+ }
187
+
188
+ # create a DNS record in route53 for instance private ip
189
+ record_name = %W(#{storage_name.downcase.dasherize} #{region} #{zone_name}).join('.')
190
+ create_single_dns_record("#{storage_name}PrivateZone",
191
+ stack_name,
192
+ zone_name,
193
+ record_name,
194
+ resource_records: [get_att(storage_resource_name, 'PrivateIp')])
195
+ end
196
+ end # module ElasticsearchBitnami
197
+ end # module Plugins
198
+ end # module Enscalator
@@ -0,0 +1,225 @@
1
+ module Enscalator
2
+ module Plugins
3
+ # Elasticsearch related configuration
4
+ module ElasticsearchOpsworks
5
+ include Enscalator::Plugins::Elb
6
+
7
+ # Create Elasticsearch cluster using Opsworks
8
+ #
9
+ # @param [String] app_name application name
10
+ # @param [String] ssh_key name of the ssh key
11
+ # @param [String] os base operating system
12
+ # @param [String] cookbook chef cookbook
13
+ def elasticsearch_init(app_name,
14
+ ssh_key:,
15
+ es_config: {},
16
+ os: 'Amazon Linux 2015.09',
17
+ cookbook: 'https://github.com/en-japan-air/opsworks-elasticsearch-cookbook.git')
18
+
19
+ parameter "ES#{app_name}ChefCookbook",
20
+ Default: cookbook,
21
+ Description: 'GitURL',
22
+ Type: 'String'
23
+
24
+ parameter "ES#{app_name}InstanceDefaultOs",
25
+ Default: os,
26
+ Description: ['The stack\'s default operating system, which is installed',
27
+ 'on every instance unless you specify a different',
28
+ 'operating system when you create the instance.'].join(' '),
29
+ Type: 'String'
30
+
31
+ parameter "ES#{app_name}SshKeyName",
32
+ Default: ssh_key,
33
+ Description: 'SSH key name for EC2 instances.',
34
+ Type: 'String'
35
+
36
+ resource 'InstanceRole',
37
+ Type: 'AWS::IAM::InstanceProfile',
38
+ Properties: {
39
+ Path: '/',
40
+ Roles: [
41
+ ref('OpsWorksEC2Role')
42
+ ]
43
+ }
44
+
45
+ resource 'ServiceRole',
46
+ Type: 'AWS::IAM::Role',
47
+ Properties: {
48
+ AssumeRolePolicyDocument: {
49
+ Statement: [
50
+ {
51
+ Effect: 'Allow',
52
+ Principal: {
53
+ Service: [
54
+ 'opsworks.amazonaws.com'
55
+ ]
56
+ },
57
+ Action: [
58
+ 'sts:AssumeRole'
59
+ ]
60
+ }
61
+ ]
62
+ },
63
+ Path: '/',
64
+ Policies: [
65
+ {
66
+ PolicyName: "#{app_name}-opsworks-service",
67
+ PolicyDocument: {
68
+ Statement: [
69
+ {
70
+ Effect: 'Allow',
71
+ Action: %w( ec2:* iam:PassRole cloudwatch:GetMetricStatistics elasticloadbalancing:* ),
72
+ Resource: '*'
73
+ }
74
+ ]
75
+ }
76
+ }
77
+ ]
78
+ }
79
+
80
+ resource 'OpsWorksEC2Role',
81
+ Type: 'AWS::IAM::Role',
82
+ Properties: {
83
+ AssumeRolePolicyDocument: {
84
+ Statement: [
85
+ {
86
+ Effect: 'Allow',
87
+ Principal: {
88
+ Service: [
89
+ 'ec2.amazonaws.com'
90
+ ]
91
+ },
92
+ Action: [
93
+ 'sts:AssumeRole'
94
+ ]
95
+ }
96
+ ]
97
+ },
98
+ Path: '/',
99
+ Policies: [
100
+ {
101
+ PolicyName: "#{app_name}-opsworks-ec2-role",
102
+ PolicyDocument: {
103
+ Statement: [
104
+ {
105
+ Effect: 'Allow',
106
+ Action: %w(
107
+ ec2:DescribeInstances
108
+ ec2:DescribeRegions
109
+ ec2:DescribeSecurityGroups
110
+ ec2:DescribeTags
111
+ cloudwatch:PutMetricData),
112
+ Resource: '*'
113
+ }
114
+ ]
115
+ }
116
+ }
117
+ ]
118
+ }
119
+
120
+ instances_security_group = security_group_vpc("ES#{app_name}", 'so that ES cluster can find other nodes', vpc.id)
121
+
122
+ ops_stack_name = "#{app_name}-ES"
123
+ resource 'ESStack',
124
+ Type: 'AWS::OpsWorks::Stack',
125
+ Properties: {
126
+ Name: ops_stack_name,
127
+ VpcId: vpc.id,
128
+ DefaultSubnetId: ref_resource_subnets.first,
129
+ ConfigurationManager: {
130
+ Name: 'Chef',
131
+ Version: '12'
132
+ },
133
+ UseCustomCookbooks: 'true',
134
+ CustomCookbooksSource: {
135
+ Type: 'git',
136
+ Url: ref("ES#{app_name}ChefCookbook")
137
+ },
138
+ DefaultOs: ref("ES#{app_name}InstanceDefaultOs"),
139
+ DefaultRootDeviceType: 'ebs',
140
+ DefaultSshKeyName: ref("ES#{app_name}SshKeyName"),
141
+ CustomJson: {
142
+ java: {
143
+ jdk_version: '8',
144
+ oracle: {
145
+ accept_oracle_download_terms: 'true'
146
+ },
147
+ accept_license_agreement: 'true',
148
+ install_flavor: 'oracle'
149
+ },
150
+ elasticsearch: {
151
+ plugins: [
152
+ 'analysis-kuromoji',
153
+ 'cloud-aws',
154
+ { name: 'elasticsearch-head', url: 'mobz/elasticsearch-head' }
155
+ ],
156
+ config: {
157
+ 'cluster.name': "#{app_name}-elasticsearch",
158
+ 'path.data': '/mnt/elasticsearch-data',
159
+ 'path.logs': '/mnt/elasticsearch-data/logs/',
160
+ 'network.bind_host': '0.0.0.0',
161
+ 'network.publish_host': '_non_loopback_',
162
+ 'index.routing.allocation.disable_allocation': 'false',
163
+ 'cloud.aws.region': region,
164
+ discovery: {
165
+ type: 'ec2',
166
+ ec2: {
167
+ groups: [ref(instances_security_group)],
168
+ tag: {
169
+ 'opsworks:stack': ops_stack_name
170
+ }
171
+ }
172
+ },
173
+ 'cluster.routing.allocation.awareness.attributes': 'rack_id'
174
+ }.merge(es_config)
175
+ }
176
+ },
177
+ ServiceRoleArn: {
178
+ 'Fn::GetAtt': %w(ServiceRole Arn)
179
+ },
180
+ DefaultInstanceProfileArn: {
181
+ 'Fn::GetAtt': %w(InstanceRole Arn)
182
+ }
183
+ }
184
+
185
+ resource 'ESLayer',
186
+ Type: 'AWS::OpsWorks::Layer',
187
+ Properties: {
188
+ StackId: ref('ESStack'),
189
+ Name: 'Search',
190
+ Type: 'custom',
191
+ Shortname: 'search',
192
+ CustomRecipes: {
193
+ Setup: %w(apt ark java layer-custom::es-opsworks)
194
+ },
195
+ EnableAutoHealing: 'true',
196
+ AutoAssignElasticIps: 'false',
197
+ AutoAssignPublicIps: 'false',
198
+ VolumeConfigurations: [
199
+ {
200
+ MountPoint: '/mnt/elasticsearch-data',
201
+ NumberOfDisks: 1,
202
+ VolumeType: 'gp2',
203
+ Size: 100
204
+ }
205
+ ],
206
+ CustomSecurityGroupIds: [
207
+ {
208
+ 'Fn::GetAtt': %W(#{instances_security_group} GroupId)
209
+ },
210
+ ref_private_security_group
211
+ ]
212
+ }
213
+
214
+ resource 'ESMainInstance',
215
+ Type: 'AWS::OpsWorks::Instance',
216
+ Properties: {
217
+ # EbsOptimized: true, # Not available for m3.medium
218
+ InstanceType: 'm3.medium',
219
+ LayerIds: [ref('ESLayer')],
220
+ StackId: ref('ESStack')
221
+ }
222
+ end
223
+ end # module Elasticsearch
224
+ end # module Plugins
225
+ end # module Enscalator
@@ -0,0 +1,139 @@
1
+ module Enscalator
2
+ module Plugins
3
+ # Internet facing ELB instance
4
+ module Elb
5
+ # Create new ELB instance
6
+ #
7
+ # @param [String, Hash] elb_name ELB instance name - can be either String or Fn::Join
8
+ # @param [Integer] web_server_port application port to which ELB redirects traffic
9
+ # @param [String] zone_name zone name attached to the vpc
10
+ # @return [String] ELB resource name
11
+ def elb_init(elb_name: join('-', aws_stack_name, 'elb'),
12
+ web_server_port: 9000,
13
+ health_check_path: '/',
14
+ zone_name: nil,
15
+ dns_record_name: "elb.#{stack_name.dasherize}.#{zone_name}",
16
+ instances: [],
17
+ ssl: false,
18
+ internal: true)
19
+
20
+ elb_resource_name = 'LoadBalancer'
21
+
22
+ parameter 'WebServerPort',
23
+ Description: 'TCP/IP Port for the web service',
24
+ Default: web_server_port,
25
+ Type: 'Number',
26
+ MinValue: '0',
27
+ MaxValue: '65535',
28
+ ConstraintDescription: 'must be an integer between 0 and 65535.'
29
+
30
+ security_group_vpc 'ELBSecurityGroup',
31
+ 'Security group of the application servers',
32
+ ref('VpcId'),
33
+ security_group_ingress: [
34
+ {
35
+ IpProtocol: 'tcp',
36
+ FromPort: '0',
37
+ ToPort: '65535',
38
+ CidrIp: '10.0.0.0/8'
39
+ }
40
+ ] + (internal ? [] : [
41
+ {
42
+ IpProtocol: 'tcp',
43
+ FromPort: '80',
44
+ ToPort: '80',
45
+ CidrIp: '0.0.0.0/0'
46
+ },
47
+ {
48
+ IpProtocol: 'tcp',
49
+ FromPort: '443',
50
+ ToPort: '443',
51
+ CidrIp: '0.0.0.0/0'
52
+ },
53
+ {
54
+ IpProtocol: 'tcp',
55
+ FromPort: '465',
56
+ ToPort: '465',
57
+ CidrIp: '0.0.0.0/0'
58
+ }
59
+ ]),
60
+ tags: {
61
+ Name: join('-', aws_stack_name, 'app', 'sg'),
62
+ Application: aws_stack_name
63
+ }
64
+
65
+ if ssl
66
+ parameter 'SSLCertificateId',
67
+ Description: 'Id of the SSL certificate (iam-servercertgetattributes -s certname)',
68
+ Type: 'String',
69
+ ConstraintDescription: 'must be a string'
70
+ end
71
+
72
+ properties = {
73
+ LoadBalancerName: elb_name,
74
+ Listeners: [
75
+ {
76
+ LoadBalancerPort: '80',
77
+ InstancePort: ref('WebServerPort'),
78
+ Protocol: 'HTTP'
79
+ }
80
+ ] + (ssl == false ? [] : [
81
+ { LoadBalancerPort: '443',
82
+ InstancePort: ref('WebServerPort'),
83
+ SSLCertificateId: ref('SSLCertificateId'),
84
+ Protocol: 'HTTPS' }
85
+ ]),
86
+ HealthCheck: {
87
+ Target: join('', 'HTTP:', ref_web_server_port, health_check_path),
88
+ HealthyThreshold: '3',
89
+ UnhealthyThreshold: '5',
90
+ Interval: '30',
91
+ Timeout: '5'
92
+ },
93
+ SecurityGroups: [ref('ELBSecurityGroup')],
94
+ Subnets: internal ? ref_application_subnets : public_subnets,
95
+ Tags: [
96
+ {
97
+ Key: 'Name',
98
+ Value: elb_name
99
+ },
100
+ {
101
+ Key: 'Application',
102
+ Value: aws_stack_name
103
+ },
104
+ {
105
+ Key: 'Network',
106
+ Value: (internal ? 'Private' : 'Public')
107
+ }
108
+ ]
109
+ }
110
+
111
+ properties[:Scheme] = 'internal' if internal
112
+ properties[:Instances] = instances if instances && !instances.empty?
113
+
114
+ resource elb_resource_name,
115
+ Type: 'AWS::ElasticLoadBalancing::LoadBalancer',
116
+ Properties: properties
117
+
118
+ # use alias target to create proper cloudformation template for Route53 side of elb configuration
119
+ alias_target = {
120
+ HostedZoneId: get_att(elb_resource_name, 'CanonicalHostedZoneNameID'),
121
+ DNSName: get_att(elb_resource_name, internal ? 'DNSName' : 'CanonicalHostedZoneName')
122
+ }
123
+
124
+ create_single_dns_record(nil,
125
+ stack_name,
126
+ zone_name,
127
+ dns_record_name,
128
+ alias_target: alias_target)
129
+
130
+ output "#{elb_resource_name}DNSName",
131
+ Description: 'LoadBalancer DNS Name',
132
+ Value: get_att(elb_resource_name, 'DNSName')
133
+
134
+ # return resource name
135
+ elb_resource_name
136
+ end
137
+ end # module Elb
138
+ end # module Plugins
139
+ end # module Enscalator