lono 5.3.4 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.cody/demo.rb +21 -21
- data/CHANGELOG.md +14 -0
- data/lib/lono.rb +1 -1
- data/lib/lono/blueprint/meta.rb +38 -0
- data/lib/lono/cfn/base.rb +19 -12
- data/lib/lono/cfn/delete.rb +2 -1
- data/lib/lono/cfn/preview/changeset.rb +14 -1
- data/lib/lono/cfn/rollback.rb +1 -1
- data/lib/lono/cfn/status.rb +1 -1
- data/lib/lono/help/blueprint.md +35 -24
- data/lib/lono/help/param.md +4 -1
- data/lib/lono/help/seed.md +13 -6
- data/lib/lono/help/summary.md +22 -8
- data/lib/lono/inspector/summary.rb +4 -3
- data/lib/lono/output_template.rb +2 -2
- data/lib/lono/s3/bucket.rb +1 -1
- data/lib/lono/seed/base.rb +1 -0
- data/lib/lono/seed/service_role.rb +11 -0
- data/lib/lono/template/dsl/builder.rb +3 -7
- data/lib/lono/template/dsl/builder/base.rb +24 -3
- data/lib/lono/template/dsl/builder/fn.rb +15 -11
- data/lib/lono/template/dsl/builder/{helper.rb → helpers.rb} +5 -4
- data/lib/lono/template/dsl/builder/helpers/param_helper.rb +33 -0
- data/lib/lono/template/dsl/builder/output.rb +4 -4
- data/lib/lono/template/dsl/builder/parameter.rb +2 -2
- data/lib/lono/template/dsl/builder/resource.rb +4 -3
- data/lib/lono/template/dsl/builder/resource/property_mover.rb +4 -0
- data/lib/lono/template/dsl/builder/syntax.rb +6 -7
- data/lib/lono/version.rb +1 -1
- data/lib/templates/blueprint/%blueprint_name%.gemspec.tt +1 -1
- data/lib/templates/blueprint/.meta/config.yml.tt +2 -1
- data/lib/templates/blueprint/README.md +1 -1
- data/lib/templates/blueprint_types/dsl/app/templates/%blueprint_name%.rb +22 -22
- data/lib/templates/skeleton/README.md +1 -1
- data/lono.gemspec +1 -1
- data/vendor/cfn-status/CHANGELOG.md +4 -0
- data/vendor/cfn-status/README.md +4 -2
- data/vendor/cfn-status/bin/console +1 -1
- data/vendor/cfn-status/cfn-status.gemspec +2 -2
- data/vendor/cfn-status/lib/cfn-status.rb +1 -1
- data/vendor/cfn-status/lib/cfn_status.rb +245 -0
- data/vendor/cfn-status/lib/{cfn → cfn_status}/aws_service.rb +1 -1
- data/vendor/cfn-status/lib/cfn_status/version.rb +3 -0
- data/vendor/cfn-status/spec/fixtures/cfn/pages/fresh/describe_stack_events-1.json +1103 -0
- data/vendor/cfn-status/spec/fixtures/cfn/pages/fresh/describe_stack_events-2.json +1104 -0
- data/vendor/cfn-status/spec/fixtures/cfn/pages/fresh/describe_stack_events-3.json +1103 -0
- data/vendor/cfn-status/spec/fixtures/cfn/pages/updating/describe_stack_events-1.json +1103 -0
- data/vendor/cfn-status/spec/fixtures/cfn/pages/updating/describe_stack_events-2.json +1104 -0
- data/vendor/cfn-status/spec/fixtures/cfn/pages/updating/describe_stack_events-3.json +1103 -0
- data/vendor/cfn-status/spec/lib/cfn_status_spec.rb +153 -0
- data/vendor/cfn-status/spec/spec_helper.rb +1 -1
- metadata +17 -8
- data/vendor/cfn-status/lib/cfn/status.rb +0 -219
- data/vendor/cfn-status/lib/cfn/status/version.rb +0 -5
- data/vendor/cfn-status/spec/cfn/status_spec.rb +0 -81
data/lib/lono/s3/bucket.rb
CHANGED
data/lib/lono/seed/base.rb
CHANGED
@@ -0,0 +1,11 @@
|
|
1
|
+
class Lono::Seed
|
2
|
+
module ServiceRole
|
3
|
+
def create_service_linked_role(aws_service_name)
|
4
|
+
iam.create_service_linked_role(
|
5
|
+
aws_service_name: aws_service_name
|
6
|
+
)
|
7
|
+
rescue Aws::IAM::Errors::InvalidInput # already exist
|
8
|
+
raise if ENV['LONO_DEBUG_SEED']
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -4,7 +4,7 @@ class Lono::Template::Dsl
|
|
4
4
|
include Lono::Template::Context::Loader
|
5
5
|
|
6
6
|
include Fn
|
7
|
-
include
|
7
|
+
include Helpers # built-in helpers
|
8
8
|
include Lono::Template::Evaluate
|
9
9
|
include Syntax
|
10
10
|
extend Memoist
|
@@ -27,12 +27,12 @@ class Lono::Template::Dsl
|
|
27
27
|
def template
|
28
28
|
load_context
|
29
29
|
evaluate_template_path(@path) # modifies @cfn
|
30
|
-
|
30
|
+
@cfn
|
31
31
|
end
|
32
32
|
memoize :template
|
33
33
|
|
34
34
|
def build_template
|
35
|
-
@results = YAML.dump(
|
35
|
+
@results = YAML.dump(@cfn)
|
36
36
|
end
|
37
37
|
|
38
38
|
def write_output
|
@@ -51,10 +51,6 @@ class Lono::Template::Dsl
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
def camelize(data)
|
55
|
-
CfnCamelizer.transform(data)
|
56
|
-
end
|
57
|
-
|
58
54
|
# Not using Lono::Template::Context because that works differently.
|
59
55
|
# That is used to load a context object that is passed to RenderMePretty's context.
|
60
56
|
# So that we can load context for params files and erb templates.
|
@@ -1,14 +1,35 @@
|
|
1
1
|
class Lono::Template::Dsl::Builder
|
2
2
|
class Base
|
3
3
|
include Fn
|
4
|
-
include
|
4
|
+
include Helpers
|
5
5
|
|
6
|
-
def initialize(*definition)
|
6
|
+
def initialize(blueprint, *definition)
|
7
|
+
@blueprint = blueprint
|
7
8
|
@definition = definition.flatten
|
8
9
|
end
|
9
10
|
|
11
|
+
private
|
10
12
|
def camelize(attributes)
|
11
|
-
|
13
|
+
blueprint_meta = Lono::Blueprint::Meta.new(@blueprint)
|
14
|
+
target_section = self.class.to_s.split('::').last.underscore
|
15
|
+
# target_section: Lono::Template::Dsl::Builder::Parameter => parameter
|
16
|
+
if blueprint_meta.auto_camelize?(target_section)
|
17
|
+
CfnCamelizer.transform(attributes)
|
18
|
+
else
|
19
|
+
stringify_keys!(attributes)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Accounts for Arrays also. ActiveSupport only works for Hashes.
|
24
|
+
def stringify_keys!(data)
|
25
|
+
case data
|
26
|
+
when Array
|
27
|
+
data.map! { |i| stringify_keys!(i) }
|
28
|
+
when Hash
|
29
|
+
data.deep_transform_keys! { |k| k.to_s }
|
30
|
+
else
|
31
|
+
data # do not transform
|
32
|
+
end
|
12
33
|
end
|
13
34
|
end
|
14
35
|
end
|
@@ -21,7 +21,7 @@ class Lono::Template::Dsl::Builder
|
|
21
21
|
# so they can also be called via fn::if
|
22
22
|
and: :array,
|
23
23
|
equals: :array,
|
24
|
-
if: :array, # special case, if is a Ruby keyword
|
24
|
+
if: :array, # special case, if is a Ruby keyword
|
25
25
|
not: :array,
|
26
26
|
or: :array,
|
27
27
|
ref: :simple,
|
@@ -29,6 +29,12 @@ class Lono::Template::Dsl::Builder
|
|
29
29
|
# These are also Ruby keywords
|
30
30
|
# keywords: and if not or
|
31
31
|
|
32
|
+
# Defines both normal method and bang method. Example: if and if!
|
33
|
+
def self.define_methods(name, &block)
|
34
|
+
define_method(name, &block)
|
35
|
+
define_method("#{name}!", &block)
|
36
|
+
end
|
37
|
+
|
32
38
|
# Note, for if function, do not flatten the args. Its arguments can be Arrays. Example:
|
33
39
|
#
|
34
40
|
# SecurityGroups:
|
@@ -42,31 +48,27 @@ class Lono::Template::Dsl::Builder
|
|
42
48
|
# Ref: ExistingSecurityGroup
|
43
49
|
FUNCTIONS.each do |name, type|
|
44
50
|
if type == :simple
|
45
|
-
|
51
|
+
define_methods(name) do |arg|
|
46
52
|
id = fn_id(name)
|
47
|
-
arg = arg.is_a?(Symbol) ? CfnCamelizer.camelize(arg) : arg
|
48
53
|
{ id => arg }
|
49
54
|
end
|
50
55
|
else # array
|
51
|
-
|
56
|
+
define_methods(name) do |*args|
|
52
57
|
id = fn_id(name)
|
53
58
|
# Note, do not flatten args for if statement as it can have Array as arguments.
|
54
59
|
args = args.flatten unless name == :if
|
55
|
-
args = args.map do |arg|
|
56
|
-
arg.is_a?(Symbol) ? CfnCamelizer.camelize(arg) : arg
|
57
|
-
end
|
58
60
|
{ id => args }
|
59
61
|
end
|
60
62
|
end
|
61
63
|
end
|
62
64
|
|
63
65
|
def fn_id(name)
|
64
|
-
"Fn::#{
|
66
|
+
"Fn::#{name.to_s.camelize}"
|
65
67
|
end
|
66
68
|
|
67
69
|
# special cases
|
68
70
|
def ref(name)
|
69
|
-
name =
|
71
|
+
name = name.to_s.camelize
|
70
72
|
{ "Ref" => name }
|
71
73
|
end
|
72
74
|
|
@@ -84,8 +86,10 @@ class Lono::Template::Dsl::Builder
|
|
84
86
|
else
|
85
87
|
item
|
86
88
|
end
|
87
|
-
list.map!(&:camelize) unless options[:autoformat] == false
|
88
|
-
|
89
|
+
# list.map!(&:camelize) unless options[:autoformat] == false # TODO: maybe add as an option.
|
90
|
+
# feel this may be to destructive since am going with auto_camelize false for resources now.
|
91
|
+
args = [list[0], list[1..-1].join('.')]
|
92
|
+
{ "Fn::GetAtt" => args }
|
89
93
|
end
|
90
94
|
|
91
95
|
def join(delimiter, *list)
|
@@ -1,14 +1,15 @@
|
|
1
1
|
# Built-in helpers for the DSL form
|
2
2
|
class Lono::Template::Dsl::Builder
|
3
|
-
module
|
3
|
+
module Helpers
|
4
4
|
extend Memoist
|
5
|
+
include ParamHelper
|
5
6
|
|
6
7
|
def tags(hash, casing: :camelize)
|
7
8
|
hash.map do |k,v|
|
8
9
|
k = k.to_s
|
9
10
|
k = case casing
|
10
11
|
when :camelize
|
11
|
-
|
12
|
+
k.camelize
|
12
13
|
when :underscore
|
13
14
|
k.underscore
|
14
15
|
when :dasherize
|
@@ -17,13 +18,13 @@ class Lono::Template::Dsl::Builder
|
|
17
18
|
k
|
18
19
|
end
|
19
20
|
|
20
|
-
{
|
21
|
+
{Key: k, Value: v}
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
24
25
|
def dimensions(hash, casing: :camelize)
|
25
26
|
tags(hash, casing: casing).map { |h|
|
26
|
-
h[:
|
27
|
+
h[:Name] = h.delete(:Key) || h.delete(:key)
|
27
28
|
h
|
28
29
|
}
|
29
30
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Note: These are experimental helpers. Their interface may change or removed entirely.
|
2
|
+
module Lono::Template::Dsl::Builder::Helpers
|
3
|
+
module ParamHelper
|
4
|
+
# Creates:
|
5
|
+
#
|
6
|
+
# 1. parameter
|
7
|
+
# 2. condition - used to make it optional
|
8
|
+
#
|
9
|
+
def conditional_parameter(name, options={})
|
10
|
+
options = normalize_conditional_parameter_options(options)
|
11
|
+
parameter(name, options)
|
12
|
+
condition("Has#{name}", not!(equals(ref(name), "")))
|
13
|
+
end
|
14
|
+
|
15
|
+
# use long name to minimize method name collision
|
16
|
+
def normalize_conditional_parameter_options(options)
|
17
|
+
if options.is_a?(Hash)
|
18
|
+
options = if options.empty?
|
19
|
+
""
|
20
|
+
else
|
21
|
+
defaults = { Default: "" }
|
22
|
+
options.reverse_merge(defaults)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
options
|
27
|
+
end
|
28
|
+
|
29
|
+
def optional_ref(name)
|
30
|
+
if!("Has#{name}", ref(name), ref("AWS::NoValue"))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -14,20 +14,20 @@ class Lono::Template::Dsl::Builder
|
|
14
14
|
if definition.size == 1 && first.is_a?(Hash) # long form
|
15
15
|
first # pass through
|
16
16
|
elsif definition.size == 2 && second.is_a?(Hash) # medium form
|
17
|
-
if second.key?(:value)
|
17
|
+
if second.key?(:value) || second.key?("Value")
|
18
18
|
logical_id, properties = first, second
|
19
19
|
else
|
20
20
|
logical_id = first
|
21
|
-
properties = {
|
21
|
+
properties = {Value: second}
|
22
22
|
end
|
23
23
|
{ logical_id => properties }
|
24
24
|
elsif definition.size == 2 && (second.is_a?(String) || second.nil?) # short form
|
25
25
|
logical_id = first
|
26
|
-
properties = second.is_a?(String) ? {
|
26
|
+
properties = second.is_a?(String) ? { Value: second } : {}
|
27
27
|
{ logical_id => properties }
|
28
28
|
elsif definition.size == 1
|
29
29
|
logical_id = first.to_s
|
30
|
-
properties = {
|
30
|
+
properties = { Value: ref(logical_id) }
|
31
31
|
{ logical_id => properties }
|
32
32
|
else # I dont know what form
|
33
33
|
raise "Invalid form provided. definition #{definition.inspect}"
|
@@ -19,7 +19,7 @@ class Lono::Template::Dsl::Builder
|
|
19
19
|
elsif (definition.size == 2 && valid_value?(second)) || # short form
|
20
20
|
definition.size == 1
|
21
21
|
logical_id = first
|
22
|
-
properties = valid_value?(second) ? {
|
22
|
+
properties = valid_value?(second) ? { Default: second } : {}
|
23
23
|
{ logical_id => properties }
|
24
24
|
else # I dont know what form
|
25
25
|
raise "Invalid form provided. definition #{definition.inspect}"
|
@@ -28,7 +28,7 @@ class Lono::Template::Dsl::Builder
|
|
28
28
|
|
29
29
|
def add_required(attributes)
|
30
30
|
properties = attributes.values.first
|
31
|
-
properties[
|
31
|
+
properties["Type"] ||= 'String'
|
32
32
|
attributes
|
33
33
|
end
|
34
34
|
|
@@ -15,7 +15,8 @@ class Lono::Template::Dsl::Builder
|
|
15
15
|
first # pass through
|
16
16
|
elsif definition.size == 2 && second.is_a?(Hash) # medium form
|
17
17
|
logical_id, attributes = first, second
|
18
|
-
attributes.delete(:properties) if attributes[:properties].
|
18
|
+
attributes.delete(:properties) if attributes[:properties].blank?
|
19
|
+
attributes.delete("Properties") if attributes["Properties"].blank?
|
19
20
|
{ logical_id => attributes }
|
20
21
|
elsif definition.size == 2 && second.is_a?(String) # short form with no properties
|
21
22
|
logical_id, type = first, second
|
@@ -25,7 +26,7 @@ class Lono::Template::Dsl::Builder
|
|
25
26
|
elsif definition.size == 3 && (second.is_a?(String) || second.is_a?(NilClass)) # short form
|
26
27
|
logical_id, type, properties = first, second, third
|
27
28
|
resource = { logical_id => {
|
28
|
-
|
29
|
+
Type: type
|
29
30
|
}}
|
30
31
|
|
31
32
|
attributes = resource.values.first
|
@@ -33,7 +34,7 @@ class Lono::Template::Dsl::Builder
|
|
33
34
|
property_mover = PropertyMover.new(resource, logical_id, properties)
|
34
35
|
property_mover.move!
|
35
36
|
|
36
|
-
attributes[
|
37
|
+
attributes["Properties"] = properties unless properties.empty?
|
37
38
|
resource
|
38
39
|
else # Dont understand this form
|
39
40
|
raise "Invalid form provided. definition #{definition.inspect}"
|
@@ -7,7 +7,11 @@ class Lono::Template::Dsl::Builder::Resource
|
|
7
7
|
|
8
8
|
def move!
|
9
9
|
%w[depends_on condition].each do |attribute_name|
|
10
|
+
# Account for camelize, underscore, String, and Symbol
|
10
11
|
move(attribute_name.to_sym)
|
12
|
+
move(attribute_name.camelize.to_sym)
|
13
|
+
move(attribute_name)
|
14
|
+
move(attribute_name.camelize)
|
11
15
|
end
|
12
16
|
end
|
13
17
|
|
@@ -15,44 +15,43 @@ class Lono::Template::Dsl::Builder
|
|
15
15
|
|
16
16
|
def transform(*definition)
|
17
17
|
definition = definition.flatten
|
18
|
-
definition.map! { |x| CfnCamelizer.camelize(x) }
|
19
18
|
@cfn["Transform"] = definition.size == 1 ? definition.first : definition
|
20
19
|
end
|
21
20
|
|
22
21
|
def parameter(*definition)
|
23
22
|
@cfn["Parameters"] ||= {}
|
24
|
-
param = Parameter.new(definition)
|
23
|
+
param = Parameter.new(@blueprint, definition)
|
25
24
|
@cfn["Parameters"].merge!(param.template)
|
26
25
|
end
|
27
26
|
|
28
27
|
def mapping(*definition)
|
29
28
|
@cfn["Mappings"] ||= {}
|
30
|
-
mapping = Mapping.new(definition)
|
29
|
+
mapping = Mapping.new(@blueprint, definition)
|
31
30
|
@cfn["Mappings"].merge!(mapping.template)
|
32
31
|
end
|
33
32
|
|
34
33
|
def resource(*definition)
|
35
34
|
@cfn["Resources"] ||= {}
|
36
|
-
resource = Resource.new(definition)
|
35
|
+
resource = Resource.new(@blueprint, definition)
|
37
36
|
@cfn["Resources"].merge!(resource.template)
|
38
37
|
end
|
39
38
|
|
40
39
|
def condition(*definition)
|
41
40
|
@cfn["Conditions"] ||= {}
|
42
|
-
condition = Condition.new(definition)
|
41
|
+
condition = Condition.new(@blueprint, definition)
|
43
42
|
@cfn["Conditions"].merge!(condition.template)
|
44
43
|
end
|
45
44
|
|
46
45
|
def output(*definition)
|
47
46
|
@cfn["Outputs"] ||= {}
|
48
|
-
output = Output.new(definition)
|
47
|
+
output = Output.new(@blueprint, definition)
|
49
48
|
@cfn["Outputs"].merge!(output.template)
|
50
49
|
end
|
51
50
|
|
52
51
|
# Generic section method in case CloudFormation adds a new future section.
|
53
52
|
# The generic section method adds a new top-level key
|
54
53
|
def section(key, definition)
|
55
|
-
@cfn[key] = Section.new(definition).template
|
54
|
+
@cfn[key] = Section.new(@blueprint, definition).template
|
56
55
|
end
|
57
56
|
end
|
58
57
|
end
|
data/lib/lono/version.rb
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
|
10
10
|
spec.summary = "Write a short summary because it's required." # TODO: Change me
|
11
11
|
spec.description = "Write a longer description or delete this line." # TODO: Change me
|
12
|
-
spec.homepage = "https://github.com/
|
12
|
+
spec.homepage = "https://github.com/USER/<%= blueprint_name %>" # TODO: Change me
|
13
13
|
spec.license = "<%= ENV['LONO_LICENSE'] || 'MIT' %>"
|
14
14
|
|
15
15
|
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
@@ -34,4 +34,4 @@ The PARAM depends on how the blueprint was authored. The PARAM conventionally d
|
|
34
34
|
Use the [lono cfn deploy](http://lono.cloud/reference/lono-cfn-deploy/) command to deploy. Example:
|
35
35
|
|
36
36
|
LONO_ENV=development lono cfn deploy BLUEPRINT-development --blueprint BLUEPRINT --iam
|
37
|
-
LONO_ENV=production lono cfn deploy BLUEPRINT-production
|
37
|
+
LONO_ENV=production lono cfn deploy BLUEPRINT-production --blueprint BLUEPRINT --iam
|
@@ -1,36 +1,36 @@
|
|
1
|
-
#
|
1
|
+
# Starter Demo Example
|
2
2
|
aws_template_format_version "2010-09-09"
|
3
3
|
description "Demo stack"
|
4
4
|
|
5
5
|
parameter("InstanceType", "t3.micro")
|
6
6
|
|
7
7
|
mapping("AmiMap",
|
8
|
-
"ap-northeast-1": {
|
9
|
-
"ap-northeast-2": {
|
10
|
-
"ap-south-1": {
|
11
|
-
"ap-southeast-1": {
|
12
|
-
"ap-southeast-2": {
|
13
|
-
"ca-central-1": {
|
14
|
-
"eu-central-1": {
|
15
|
-
"eu-north-1": {
|
16
|
-
"eu-west-1": {
|
17
|
-
"eu-west-2": {
|
18
|
-
"eu-west-3": {
|
19
|
-
"sa-east-1": {
|
20
|
-
"us-east-1": {
|
21
|
-
"us-east-2": {
|
22
|
-
"us-west-1": {
|
23
|
-
"us-west-2": {
|
8
|
+
"ap-northeast-1": { Ami: "ami-0f9ae750e8274075b" },
|
9
|
+
"ap-northeast-2": { Ami: "ami-047f7b46bd6dd5d84" },
|
10
|
+
"ap-south-1": { Ami: "ami-0889b8a448de4fc44" },
|
11
|
+
"ap-southeast-1": { Ami: "ami-0b419c3a4b01d1859" },
|
12
|
+
"ap-southeast-2": { Ami: "ami-04481c741a0311bbb" },
|
13
|
+
"ca-central-1": { Ami: "ami-03338e1f67dae0168" },
|
14
|
+
"eu-central-1": { Ami: "ami-09def150731bdbcc2" },
|
15
|
+
"eu-north-1": { Ami: "ami-d16fe6af" },
|
16
|
+
"eu-west-1": { Ami: "ami-07683a44e80cd32c5" },
|
17
|
+
"eu-west-2": { Ami: "ami-09ead922c1dad67e4" },
|
18
|
+
"eu-west-3": { Ami: "ami-0451ae4fd8dd178f7" },
|
19
|
+
"sa-east-1": { Ami: "ami-0669a96e355eac82f" },
|
20
|
+
"us-east-1": { Ami: "ami-0de53d8956e8dcf80" },
|
21
|
+
"us-east-2": { Ami: "ami-02bcbb802e03574ba" },
|
22
|
+
"us-west-1": { Ami: "ami-0019ef04ac50be30f" },
|
23
|
+
"us-west-2": { Ami: "ami-061392db613a6357b" }
|
24
24
|
)
|
25
25
|
|
26
26
|
resource("Instance", "AWS::EC2::Instance",
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
InstanceType: ref("InstanceType"),
|
28
|
+
ImageId: find_in_map("AmiMap", ref("AWS::Region"), "Ami"),
|
29
|
+
SecurityGroupIds: [get_att("SecurityGroup.GroupId")],
|
30
|
+
UserData: base64(user_data("bootstrap.sh"))
|
31
31
|
)
|
32
32
|
resource("SecurityGroup", "AWS::EC2::SecurityGroup",
|
33
|
-
|
33
|
+
GroupDescription: "demo security group",
|
34
34
|
)
|
35
35
|
|
36
36
|
output("Instance")
|