cfndsl-pipeline 0.1.2 → 0.1.4
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.
- checksums.yaml +4 -4
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +33 -0
- data/.travis.yml +10 -0
- data/Gemfile +7 -0
- data/Guardfile +5 -0
- data/LICENCE +21 -0
- data/README.md +58 -0
- data/Rakefile +6 -0
- data/bin/cfndsl_pipeline +16 -14
- data/cfndsl-pipeline.gemspec +40 -0
- data/examples/common_definitions.yaml +63 -0
- data/examples/pipeline_multple_files.rb +42 -0
- data/examples/pipeline_single_file.rb +54 -0
- data/examples/s3bucket.rb +316 -0
- data/examples/s3bucket.yaml +241 -0
- data/examples/standard_tags.yaml +41 -0
- data/lib/cfndsl-pipeline.rb +15 -15
- data/lib/cli_options.rb +124 -0
- data/lib/exec_cfn_nag.rb +58 -0
- data/lib/{run-cfndsl.rb → exec_cfndsl.rb} +7 -5
- data/lib/exec_syntax.rb +87 -0
- data/lib/{monkey_patches.rb → monkey-patches/cfndsl_patch.rb} +40 -24
- data/lib/{stdout_capture.rb → monkey-patches/stdout_capture.rb} +0 -0
- data/lib/options.rb +10 -4
- data/lib/params.rb +6 -5
- data/lib/version.rb +2 -1
- data/spec/cfndsl/pipeline_spec.rb +11 -0
- data/spec/spec_helper.rb +15 -0
- metadata +119 -30
- data/lib/run-cfn_nag.rb +0 -38
- data/lib/run-syntax.rb +0 -89
@@ -0,0 +1,41 @@
|
|
1
|
+
---
|
2
|
+
|
3
|
+
TagStandard:
|
4
|
+
ServiceName:
|
5
|
+
Default: axt
|
6
|
+
Type: String
|
7
|
+
LogicalName: pServiceName
|
8
|
+
Label: Service pServiceName
|
9
|
+
EnvironmentID:
|
10
|
+
Default: dev1
|
11
|
+
AllowedPattern: (^dev|^sit|^prod)([-])?(\d{1,2})
|
12
|
+
Type: String
|
13
|
+
LogicalName: pEnvironmentID
|
14
|
+
Label: Environment ID
|
15
|
+
CostCentre:
|
16
|
+
Default: CC_DEVOPS
|
17
|
+
Type: String
|
18
|
+
LogicalName: pCostCentre
|
19
|
+
AllowedPattern: '[a-zA-Z0-9+-=._:/@ ]*'
|
20
|
+
Label: Cost Centre
|
21
|
+
Owner:
|
22
|
+
Default: 'Jane Doe'
|
23
|
+
Type: String
|
24
|
+
LogicalName: pOwner
|
25
|
+
Label: Owner
|
26
|
+
ApplicationID:
|
27
|
+
Default: CMDB-NS2010
|
28
|
+
Type: String
|
29
|
+
LogicalName: pApplicationID
|
30
|
+
Label:
|
31
|
+
AppCategory:
|
32
|
+
Default: B
|
33
|
+
Type: String
|
34
|
+
AllowedPattern: '[ABCD]'
|
35
|
+
LogicalName: pAppCategory
|
36
|
+
Label: Application Category
|
37
|
+
SupportGroup:
|
38
|
+
Default: 'DevOps Support'
|
39
|
+
Type: String
|
40
|
+
LogicalName: pSupportGroup
|
41
|
+
Label: Support Group
|
data/lib/cfndsl-pipeline.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
#
|
3
|
-
#
|
4
3
|
# The MIT License
|
5
4
|
#
|
6
5
|
# Copyright (c) 2019 Cam Maxwell (cameron.maxwell@gmail.com)
|
@@ -22,44 +21,45 @@
|
|
22
21
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
22
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
24
23
|
# THE SOFTWARE.
|
25
|
-
#
|
24
|
+
#
|
26
25
|
|
27
26
|
require 'cfndsl'
|
27
|
+
require 'cfn-nag'
|
28
28
|
require 'fileutils'
|
29
29
|
|
30
|
+
require_relative 'monkey-patches/cfndsl_patch'
|
31
|
+
require_relative 'monkey-patches/stdout_capture'
|
30
32
|
require_relative 'options'
|
31
33
|
require_relative 'params'
|
32
|
-
require_relative '
|
33
|
-
require_relative '
|
34
|
-
require_relative '
|
35
|
-
require_relative 'run-cfn_nag'
|
36
|
-
require_relative 'run-syntax'
|
34
|
+
require_relative 'exec_cfndsl'
|
35
|
+
require_relative 'exec_cfn_nag'
|
36
|
+
require_relative 'exec_syntax'
|
37
37
|
|
38
38
|
module CfnDslPipeline
|
39
|
+
# Main pipeline
|
39
40
|
class Pipeline
|
40
|
-
|
41
41
|
attr_accessor :input_filename, :output_dir, :options, :base_name, :template, :output_filename, :output_file, :syntax_report
|
42
42
|
|
43
|
-
def initialize
|
43
|
+
def initialize(output_dir, options)
|
44
44
|
self.input_filename = ''
|
45
45
|
self.output_file = nil
|
46
46
|
self.template = nil
|
47
47
|
self.options = options || nil
|
48
48
|
self.syntax_report = []
|
49
49
|
FileUtils.mkdir_p output_dir
|
50
|
-
abort "Could not create output directory #{output_dir}"
|
50
|
+
abort "Could not create output directory #{output_dir}" unless Dir[output_dir]
|
51
51
|
self.output_dir = output_dir
|
52
52
|
end
|
53
53
|
|
54
54
|
def build(input_filename, cfndsl_extras)
|
55
|
-
abort "Input file #{input_filename} doesn't exist!"
|
55
|
+
abort "Input file #{input_filename} doesn't exist!" unless File.file?(input_filename)
|
56
56
|
self.input_filename = "#{input_filename}"
|
57
57
|
self.base_name = File.basename(input_filename, '.*')
|
58
|
-
self.output_filename = File.expand_path("#{
|
58
|
+
self.output_filename = File.expand_path("#{output_dir}/#{base_name}.yaml")
|
59
59
|
exec_cfndsl cfndsl_extras
|
60
|
-
exec_syntax_validation if
|
61
|
-
exec_dump_params if
|
62
|
-
exec_cfn_nag if
|
60
|
+
exec_syntax_validation if options.validate_syntax
|
61
|
+
exec_dump_params if options.dump_deploy_params
|
62
|
+
exec_cfn_nag if options.validate_cfn_nag
|
63
63
|
end
|
64
64
|
end
|
65
65
|
end
|
data/lib/cli_options.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
module CfnDslPipeline
|
5
|
+
# Command Line Options processing
|
6
|
+
class CliOptions
|
7
|
+
attr_accessor :output, :template, :pipeline, :cfndsl_extras, :op
|
8
|
+
|
9
|
+
USAGE = "Usage: #{File.basename(__FILE__)} input file [ -o output_dir ] [ -b bucket ] OPTIONS [ include1 include2 etc.. ]"
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@output = './'
|
13
|
+
@cfndsl_extras = []
|
14
|
+
@pipeline = CfnDslPipeline::Options.new
|
15
|
+
parse && validate
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# rubocop:disable Metrics/AbcSize
|
21
|
+
# rubocop:disable Metrics/MethodLength
|
22
|
+
def parse
|
23
|
+
@op = OptionParser.new do |opts|
|
24
|
+
opts.banner = USAGE
|
25
|
+
|
26
|
+
opts.on('-o', '--output dir', 'Optional output directory. Default is current directory') do |dir|
|
27
|
+
@output = dir
|
28
|
+
end
|
29
|
+
|
30
|
+
opts.on('-b', '--bucket', 'Optional Existing S3 bucket for cost estimation and large template syntax validation') do |bucket|
|
31
|
+
pipeline.validation_bucket = bucket
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on('-p', '--params', 'Create cloudformation deploy compatible params file') do
|
35
|
+
pipeline.dump_deploy_params = true
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on('-s', '--syntax', 'Enable syntax check') do
|
39
|
+
pipeline.validate_syntax = true
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on('--syntax-report', 'Save template syntax report') do
|
43
|
+
pipeline.save_syntax_report = true
|
44
|
+
end
|
45
|
+
|
46
|
+
opts.on('-a', '--audit', 'Enable cfn_nag audit') do
|
47
|
+
pipeline.validate_cfn_nag = false
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.on('--audit-rule-dir', 'cfn_nag audit custom rules directory') do
|
51
|
+
pipeline.cfn_nag[:rule_directory] = true
|
52
|
+
end
|
53
|
+
|
54
|
+
opts.on('--audit-report', 'Save cfn_nag audit report') do
|
55
|
+
pipeline.save_audit_report = true
|
56
|
+
end
|
57
|
+
|
58
|
+
opts.on('--audit-debug', 'Enable cfn_nag rule debug output') do
|
59
|
+
pipeline.debug_audit = true
|
60
|
+
end
|
61
|
+
|
62
|
+
opts.on('-e', '--estimate-costs', 'Generate URL for AWS simple cost calculator') do
|
63
|
+
pipeline.estimate_cost = true
|
64
|
+
end
|
65
|
+
|
66
|
+
opts.on('-r', '--aws-region', 'AWS region to use. Default: ap-southeast-2') do |region|
|
67
|
+
pipeline.aws_region = region
|
68
|
+
end
|
69
|
+
|
70
|
+
opts.on_tail('-h', '--help', 'show this message') do
|
71
|
+
puts opts
|
72
|
+
exit
|
73
|
+
end
|
74
|
+
|
75
|
+
opts.on_tail('-d', '--debug', 'show pipeline debug messages') do
|
76
|
+
pipeline.debug = true
|
77
|
+
exit
|
78
|
+
end
|
79
|
+
|
80
|
+
opts.on_tail('-v', '--version', 'Show version') do
|
81
|
+
puts CfnDsl::Pipeline::VERSION
|
82
|
+
exit
|
83
|
+
end
|
84
|
+
end
|
85
|
+
@op.parse!
|
86
|
+
|
87
|
+
# first non-dash parameter is the mandatory input file
|
88
|
+
@template = ARGV.pop
|
89
|
+
|
90
|
+
ARGV.each do |arg|
|
91
|
+
@cfndsl_extras << [:yaml, arg]
|
92
|
+
end if ARGV.length > 0
|
93
|
+
|
94
|
+
pipeline
|
95
|
+
end
|
96
|
+
# rubocop:enable Metrics/AbcSize
|
97
|
+
# rubocop:enable Metrics/MethodLength
|
98
|
+
|
99
|
+
def fatal(msg)
|
100
|
+
puts msg
|
101
|
+
puts @op
|
102
|
+
exit 1
|
103
|
+
end
|
104
|
+
|
105
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
106
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
107
|
+
def validate
|
108
|
+
# Exit on invalid option combinations
|
109
|
+
fatal 'Error: Input template file does not exist.' unless @template && File.file?(@template)
|
110
|
+
|
111
|
+
if @pipeline.save_syntax_report
|
112
|
+
fatal 'Error: save syntax report is set, but syntax validation was not enabled.' unless @pipeline.validate_syntax && !@pipeline.save_syntax_report
|
113
|
+
end
|
114
|
+
# rubocop:disable Style/GuardClause
|
115
|
+
if @pipeline.cfn_nag.rule_directory || @pipeline.debug_audit || @pipeline.save_audit_report
|
116
|
+
fatal 'Error: Audit options set, but audit was not enabled' unless @pipeline.validate_cfn_nag
|
117
|
+
fatal 'Error: cfn_nag rule directory does not exist' unless File.directory?(@pipeline.cfn_nag.rule_directory)
|
118
|
+
end
|
119
|
+
# rubocop:enable Style/GuardClause
|
120
|
+
end
|
121
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
122
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
123
|
+
end
|
124
|
+
end
|
data/lib/exec_cfn_nag.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'cfn-nag'
|
2
|
+
require 'logging'
|
3
|
+
require 'colorize'
|
4
|
+
|
5
|
+
module CfnDslPipeline
|
6
|
+
# Add cfn_nag functions to pipeline
|
7
|
+
class Pipeline
|
8
|
+
def exec_cfn_nag
|
9
|
+
puts 'Auditing template with cfn-nag...'
|
10
|
+
configure_cfn_nag_logging
|
11
|
+
cfn_nag = CfnNag.new(config: options.cfn_nag)
|
12
|
+
result = cfn_nag.audit(cloudformation_string: template)
|
13
|
+
save_report result
|
14
|
+
display_report result
|
15
|
+
show_summary result
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def configure_cfn_nag_logging
|
21
|
+
CfnNagLogging.configure_logging([:debug]) if options.debug_audit
|
22
|
+
end
|
23
|
+
|
24
|
+
def display_report(result)
|
25
|
+
ColoredStdoutResults.new.render([
|
26
|
+
{
|
27
|
+
filename: "#{@base_name}",
|
28
|
+
file_results: result
|
29
|
+
}
|
30
|
+
])
|
31
|
+
end
|
32
|
+
|
33
|
+
def save_report(result)
|
34
|
+
return unless options.save_audit_report
|
35
|
+
report_data = Capture.capture do
|
36
|
+
SimpleStdoutResults.new.render([
|
37
|
+
{
|
38
|
+
filename: "#{@base_name}",
|
39
|
+
file_results: result
|
40
|
+
}
|
41
|
+
])
|
42
|
+
end
|
43
|
+
filename = "#{output_dir}/#{base_name}.audit"
|
44
|
+
File.open(File.expand_path(filename), 'w').puts report_data['stdout']
|
45
|
+
puts "Saved audit report to #{filename}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def show_summary(result)
|
49
|
+
if result[:failure_count] > 0
|
50
|
+
puts "Audit failed. #{result[:failure_count]} error(s) found ( ಠ ʖ̯ ಠ) ".red
|
51
|
+
elsif result[:violations].count > 0
|
52
|
+
puts "Audit passed with #{result[:warning_count]} warnings. (._.) ".yellow
|
53
|
+
else
|
54
|
+
puts 'Audit passed! ヽ( ゚ヮ゚)/ ヽ(´ー`)ノ'.green
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -3,16 +3,18 @@ require 'cfndsl/globals'
|
|
3
3
|
require 'cfndsl/version'
|
4
4
|
|
5
5
|
module CfnDslPipeline
|
6
|
+
#
|
6
7
|
class Pipeline
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
def exec_cfndsl(cfndsl_extras)
|
9
|
+
puts 'Generating CloudFormation template...'
|
10
|
+
|
11
|
+
model = CfnDsl.eval_file_with_extras("#{@input_filename}", cfndsl_extras, (options.debug_cfndsl ? STDOUT : nil))
|
10
12
|
@template = JSON.parse(model.to_json).to_yaml
|
11
13
|
File.open(@output_filename, 'w') do |file|
|
12
14
|
file.puts @template
|
13
15
|
end
|
14
16
|
@output_file = File.open(@output_filename)
|
15
|
-
puts "
|
17
|
+
puts "#{@output_file.size} bytes written to #{@output_filename}"
|
16
18
|
end
|
17
19
|
end
|
18
|
-
end
|
20
|
+
end
|
data/lib/exec_syntax.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'aws-sdk-cloudformation'
|
3
|
+
require 'aws-sdk-s3'
|
4
|
+
require 'uuid'
|
5
|
+
|
6
|
+
module CfnDslPipeline
|
7
|
+
#
|
8
|
+
class Pipeline
|
9
|
+
attr_accessor :s3_client
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
self.s3_client = Aws::S3::Client.new(region: aws_region)
|
13
|
+
end
|
14
|
+
# rubocop:disable Metrics/AbcSize
|
15
|
+
def exec_syntax_validation
|
16
|
+
puts 'Validating template syntax...'
|
17
|
+
if options.estimate_cost || (output_file.size > 51_200)
|
18
|
+
puts 'Filesize is greater than 51200, or cost estimation required. Validating via S3 bucket '
|
19
|
+
uuid = UUID.new
|
20
|
+
bucket = determine_bucket
|
21
|
+
object_name = uuid.generate.to_s
|
22
|
+
upload_template(bucket, object_name)
|
23
|
+
self.syntax_report = s3_validate_syntax(bucket, object_name)
|
24
|
+
estimate_cost(bucket_name, object_name)
|
25
|
+
unless options.validation_bucket
|
26
|
+
puts 'Deleting temporary S3 bucket...'
|
27
|
+
bucket.delete!
|
28
|
+
end
|
29
|
+
else
|
30
|
+
self.syntax_report = local_validate_syntax
|
31
|
+
end
|
32
|
+
save_syntax_report
|
33
|
+
end
|
34
|
+
# rubocop:enable Metrics/AbcSize
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def determine_bucket
|
39
|
+
if options.validation_bucket
|
40
|
+
bucket_name = options.validation_bucket
|
41
|
+
puts "Using existing S3 bucket #{bucket_name}..."
|
42
|
+
bucket = s3_client.bucket(options.validation_bucket)
|
43
|
+
else
|
44
|
+
bucket_name = "arch-code-#{uuid.generate}"
|
45
|
+
puts "Creating temporary S3 bucket #{bucket_name}..."
|
46
|
+
bucket = s3_client.bucket(bucket_name)
|
47
|
+
bucket.create
|
48
|
+
end
|
49
|
+
bucket
|
50
|
+
end
|
51
|
+
|
52
|
+
def save_syntax_report
|
53
|
+
return unless options.save_syntax_report
|
54
|
+
report_filename = "#{output_dir}/#{base_name}.report"
|
55
|
+
puts "Syntax validation report written to #{report_filename}"
|
56
|
+
File.open(File.expand_path(report_filename), 'w').puts syntax_report.to_hash.to_yaml
|
57
|
+
end
|
58
|
+
|
59
|
+
def upload_template(bucket, object_name)
|
60
|
+
puts 'Uploading template to temporary S3 bucket...'
|
61
|
+
object = bucket.object(object_name)
|
62
|
+
object.upload_file(output_file)
|
63
|
+
puts "https://s3.amazonaws.com/#{bucket_name}/#{object_name}"
|
64
|
+
end
|
65
|
+
|
66
|
+
def estimate_cost(bucket, object_name)
|
67
|
+
return unless options.estimate_cost
|
68
|
+
puts 'Estimate cost of template...'
|
69
|
+
client = Aws::CloudFormation::Client.new(region: options.aws_region)
|
70
|
+
costing = client.estimate_template_cost(template_url: "https://#{bucket.url}/#{object_name}")
|
71
|
+
puts "Cost Calculator URL is: #{costing.url}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def s3_validate_syntax(bucket, object_name)
|
75
|
+
return unless options.validate_syntax
|
76
|
+
puts 'Validating template syntax in S3 Bucket...'
|
77
|
+
client = Aws::CloudFormation::Client.new(region: options.aws_region)
|
78
|
+
client.validate_template(template_url: "https://s3.amazonaws.com/#{bucket.url}/#{object_name}")
|
79
|
+
end
|
80
|
+
|
81
|
+
def local_validate_syntax
|
82
|
+
puts 'Validating template syntax locally...'
|
83
|
+
client = Aws::CloudFormation::Client.new(region: options.aws_region)
|
84
|
+
client.validate_template(template_body: template)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -1,10 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
2
|
require 'cfndsl/globals'
|
4
3
|
require 'cfndsl/version'
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
require 'cfndsl/external_parameters'
|
5
|
+
require 'cfndsl/aws/cloud_formation_template'
|
6
|
+
|
7
|
+
PARAM_PROPS = %w([Description Default AllowedPattern AllowedValues]).freeze
|
8
|
+
HAS_PROPAGATABLE_TAGS = %w([CfnDsl::AWS::Types::AWS_AutoScaling_AutoScalingGroup]).freeze
|
9
|
+
# rubocop:disable Metrics/LineLength
|
10
|
+
HAS_MAPPED_TAGS = %w([CfnDsl::AWS::Types::AWS_Serverless_Function CfnDsl::AWS::Types::AWS_Serverless_SimpleTable CfnDsl::AWS::Types::AWS_Serverless_Application]).freeze
|
11
|
+
# rubocop:enable Metrics/LineLength
|
8
12
|
|
9
13
|
# Automatically add Parameters for Tag values
|
10
14
|
CfnDsl::CloudFormationTemplate.class_eval do
|
@@ -21,11 +25,20 @@ CfnDsl::CloudFormationTemplate.class_eval do
|
|
21
25
|
send(key, props[key]) if props[key]
|
22
26
|
end
|
23
27
|
end
|
24
|
-
end if external_parameters[:TagStandard].
|
28
|
+
end if external_parameters[:TagStandard].is_a?(Hash)
|
25
29
|
end
|
26
30
|
end
|
27
31
|
|
28
32
|
module CfnDsl
|
33
|
+
# Add ability to reset params when being used in loops in rakefiles etc
|
34
|
+
class ExternalParameters
|
35
|
+
class << self
|
36
|
+
def reset
|
37
|
+
@parameters = self.class.defaults.clone
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
29
42
|
# extends CfnDsl esource Properties to automatically substitute
|
30
43
|
# FnSub recuraively
|
31
44
|
class PropertyDefinition < JSONable
|
@@ -36,9 +49,8 @@ module CfnDsl
|
|
36
49
|
def fix_substitutions(val)
|
37
50
|
return val unless defined? val.class.to_s.downcase
|
38
51
|
meth = "fix_#{val.class.to_s.downcase}"
|
39
|
-
|
40
|
-
|
41
|
-
end
|
52
|
+
|
53
|
+
return send(meth, val) if respond_to?(meth.to_sym)
|
42
54
|
val
|
43
55
|
end
|
44
56
|
|
@@ -62,27 +74,31 @@ module CfnDsl
|
|
62
74
|
apply_tag_standard
|
63
75
|
end
|
64
76
|
|
77
|
+
private
|
78
|
+
|
65
79
|
def apply_tag_standard
|
66
80
|
return unless defined? external_parameters[:TagStandard]
|
67
|
-
return unless external_parameters[:TagStandard].
|
68
|
-
|
69
|
-
|
81
|
+
return unless external_parameters[:TagStandard].is_a?(Hash)
|
82
|
+
apply_tags(external_parameters[:TagStandard]) if defined? self.Tag
|
83
|
+
apply_tags_map(external_parameters[:TagStandard]) if HAS_MAPPED_TAGS.include? self.class.to_s
|
84
|
+
end
|
70
85
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
end
|
78
|
-
end
|
79
|
-
elsif HAS_MAPPED_TAGS.include? resource_type
|
80
|
-
tag_map = {}
|
81
|
-
external_parameters[:TagStandard].each do |tag_name, props|
|
82
|
-
tag_map[tag_name.to_s] = Ref(props['LogicalName'] || tag_name)
|
86
|
+
def apply_tags(tags)
|
87
|
+
tags.each do |tag_name, props|
|
88
|
+
send(:Tag) do
|
89
|
+
Key tag_name.to_s
|
90
|
+
Value Ref(props['LogicalName'] || tag_name)
|
91
|
+
PropagateAtLaunch true if HAS_PROPAGATABLE_TAGS.include? self.class.to_s
|
83
92
|
end
|
84
|
-
Tags tag_map
|
85
93
|
end
|
86
94
|
end
|
95
|
+
|
96
|
+
def apply_tags_map(tags)
|
97
|
+
tag_map = {}
|
98
|
+
tags.each do |tag_name, props|
|
99
|
+
tag_map[tag_name.to_s] = Ref(props['LogicalName'] || tag_name)
|
100
|
+
end
|
101
|
+
Tags tag_map
|
102
|
+
end
|
87
103
|
end
|
88
104
|
end
|