class CfnDslPipeline::Pipeline

Main pipeline

Interface to cfn_nag auditing

Interface to cfndsl

Interface to AWS SDK syntax checks

Dump stack parameters based on template

Attributes

base_name[RW]
input_filename[RW]
options[RW]
output_dir[RW]
output_file[RW]
output_filename[RW]
s3_client[RW]
syntax_report[RW]
template[RW]

Public Class Methods

new(output_dir, options) click to toggle source
# File lib/cfndsl-pipeline.rb, line 44
def initialize(output_dir, options)
  self.input_filename = ''
  self.output_file = nil
  self.template = nil
  self.options = options || nil
  self.syntax_report = []
  FileUtils.mkdir_p output_dir
  abort "Could not create output directory #{output_dir}" unless Dir[output_dir]
  self.output_dir = output_dir
end

Public Instance Methods

build(input_filename, cfndsl_extras) click to toggle source
# File lib/cfndsl-pipeline.rb, line 55
def build(input_filename, cfndsl_extras)
  abort "Input file #{input_filename} doesn't exist!" unless File.file?(input_filename)
  self.input_filename = input_filename.to_s
  self.base_name = File.basename(input_filename, '.*')
  self.output_filename = File.expand_path("#{output_dir}/#{base_name}.yaml")
  exec_cfndsl cfndsl_extras
  exec_syntax_validation if options.validate_syntax
  exec_dump_params if options.dump_deploy_params
  exec_cfn_nag if options.validate_cfn_nag
end
exec_cfn_nag() click to toggle source
# File lib/exec_cfn_nag.rb, line 10
def exec_cfn_nag
  puts 'Auditing template with cfn-nag...'
  configure_cfn_nag_logging
  cfn_nag = CfnNag.new(config: options.cfn_nag)
  result = cfn_nag.audit(cloudformation_string: template)
  save_report result
  display_report result
  show_summary result
end
exec_cfndsl(cfndsl_extras) click to toggle source
# File lib/exec_cfndsl.rb, line 10
def exec_cfndsl(cfndsl_extras)
  puts 'Generating CloudFormation template...'

  model = CfnDsl.eval_file_with_extras(@input_filename.to_s, cfndsl_extras, (options.debug_cfndsl ? STDOUT : nil))
  @template = JSON.parse(model.to_json).to_yaml
  File.open(@output_filename, 'w') do |file|
    file.puts @template
  end
  @output_file = File.open(@output_filename)
  puts "#{@output_file.size} bytes written to #{@output_filename}"
end
exec_dump_params() click to toggle source
# File lib/params.rb, line 8
def exec_dump_params
  param_filename = "#{output_dir}/#{base_name}.params"
  puts "Deploy parameters written to #{param_filename}"
  param_file = File.open(File.expand_path(param_filename), 'w')
  syntax_report['parameters'].each do |param|
    param_file.puts "#{param['parameter_key']}=#{param['default_value']}"
  end
end
exec_syntax_validation() click to toggle source
# File lib/exec_syntax.rb, line 16
def exec_syntax_validation
  puts 'Validating template syntax...'
  if options.estimate_cost || (output_file.size > 51_200)
    puts 'Filesize is greater than 51200, or cost estimation required. Validating via S3 bucket '
    uuid = UUID.new
    bucket = determine_bucket
    object_name = uuid.generate.to_s
    upload_template(bucket, object_name)
    self.syntax_report = s3_validate_syntax(bucket, object_name)
    estimate_cost(bucket_name, object_name)
    unless options.validation_bucket
      puts 'Deleting temporary S3 bucket...'
      bucket.delete!
    end
  else
    self.syntax_report = local_validate_syntax
  end
  save_syntax_report
end

Private Instance Methods

configure_cfn_nag_logging() click to toggle source
# File lib/exec_cfn_nag.rb, line 22
def configure_cfn_nag_logging
  CfnNagLogging.configure_logging([:debug]) if options.debug_audit
end
determine_bucket() click to toggle source
# File lib/exec_syntax.rb, line 38
def determine_bucket
  if options.validation_bucket
    bucket_name = options.validation_bucket
    puts "Using existing S3 bucket #{bucket_name}..."
    bucket = s3_client.bucket(options.validation_bucket)
  else
    bucket_name = "arch-code-#{uuid.generate}"
    puts "Creating temporary S3 bucket #{bucket_name}..."
    bucket = s3_client.bucket(bucket_name)
    bucket.create
  end
  bucket
end
display_report(result) click to toggle source
# File lib/exec_cfn_nag.rb, line 26
def display_report(result)
  ColoredStdoutResults.new.render(
    [
      {
        filename: @base_name.to_s,
        file_results: result
      }
    ]
  )
end
estimate_cost(bucket, object_name) click to toggle source
# File lib/exec_syntax.rb, line 67
def estimate_cost(bucket, object_name)
  return unless options.estimate_cost

  puts 'Estimate cost of template...'
  client = Aws::CloudFormation::Client.new(region: options.aws_region)
  costing = client.estimate_template_cost(template_url: "https://#{bucket.url}/#{object_name}")
  puts "Cost Calculator URL is: #{costing.url}"
end
local_validate_syntax() click to toggle source
# File lib/exec_syntax.rb, line 84
def local_validate_syntax
  puts 'Validating template syntax locally...'
  client = Aws::CloudFormation::Client.new(region: options.aws_region)
  client.validate_template(template_body: template)
end
s3_validate_syntax(bucket, object_name) click to toggle source
# File lib/exec_syntax.rb, line 76
def s3_validate_syntax(bucket, object_name)
  return unless options.validate_syntax

  puts 'Validating template syntax in S3 Bucket...'
  client = Aws::CloudFormation::Client.new(region: options.aws_region)
  client.validate_template(template_url: "https://s3.amazonaws.com/#{bucket.url}/#{object_name}")
end
save_report(result) click to toggle source
# File lib/exec_cfn_nag.rb, line 37
def save_report(result)
  return unless options.save_audit_report

  report_data = Capture.capture do
    SimpleStdoutResults.new.render(
      [
        {
          filename: @base_name.to_s,
          file_results: result
        }
      ]
    )
  end
  filename = "#{output_dir}/#{base_name}.audit"
  File.open(File.expand_path(filename), 'w').puts report_data['stdout']
  puts "Saved audit report to #{filename}"
end
save_syntax_report() click to toggle source
# File lib/exec_syntax.rb, line 52
def save_syntax_report
  return unless options.save_syntax_report

  report_filename = "#{output_dir}/#{base_name}.report"
  puts "Syntax validation report written to #{report_filename}"
  File.open(File.expand_path(report_filename), 'w').puts syntax_report.to_hash.to_yaml
end
show_summary(result) click to toggle source
# File lib/exec_cfn_nag.rb, line 55
def show_summary(result)
  if result[:failure_count].positive?
    puts "Audit failed. #{result[:failure_count]} error(s) found     ( ಠ ʖ̯ ಠ)  ".red
  elsif result[:violations].count.positive?
    puts "Audit passed with #{result[:warning_count]} warnings.     (._.)  ".yellow
  else
    puts 'Audit passed!        ヽ( ゚ヮ゚)/      ヽ(´ー`)ノ'.green
  end
end
upload_template(bucket, object_name) click to toggle source
# File lib/exec_syntax.rb, line 60
def upload_template(bucket, object_name)
  puts 'Uploading template to temporary S3 bucket...'
  object = bucket.object(object_name)
  object.upload_file(output_file)
  puts "https://s3.amazonaws.com/#{bucket_name}/#{object_name}"
end