class CfnGuardian::Cli

Public Instance Methods

__print_version() click to toggle source
# File lib/cfnguardian.rb, line 21
def __print_version
  puts CfnGuardian::VERSION
end
compile() click to toggle source
# File lib/cfnguardian.rb, line 44
def compile
  set_log_level(options[:debug])
  
  set_region(options[:region],options[:validate])
  s3 = CfnGuardian::S3.new(options[:bucket],options[:path])
  
  compiler = CfnGuardian::Compile.new(options[:config])
  compiler.get_resources
  compiler.compile_templates(s3.bucket,s3.path)
  logger.info "Cloudformation templates compiled successfully in out/ directory"
  if options[:validate]
    s3.create_bucket_if_not_exists()
    validator = CfnGuardian::Validate.new(s3.bucket)
    if validator.validate
      logger.error("One or more templates failed to validate")
      exit(1)
    else
      logger.info "Cloudformation templates were validated successfully"
    end
  end
  logger.warn "AWS cloudwatch alarms defined in the templates will cost roughly $#{'%.2f' % compiler.cost} per month"

  if options[:template_config]
    logger.info "Generating a AWS CodePipeline template configuration file template-config.guardian.json"
    parameters = compiler.load_parameters(options)
    compiler.genrate_template_config(parameters)
  end
end
deploy() click to toggle source
# File lib/cfnguardian.rb, line 89
def deploy
  set_log_level(options[:debug])
  
  set_region(options[:region],true)
  s3 = CfnGuardian::S3.new(options[:bucket],options[:path])
  
  compiler = CfnGuardian::Compile.new(options[:config])
  compiler.get_resources
  compiler.compile_templates(s3.bucket,s3.path)
  parameters = compiler.load_parameters(options)
  logger.info "Cloudformation templates compiled successfully in out/ directory"

  s3.create_bucket_if_not_exists
  validator = CfnGuardian::Validate.new(s3.bucket)
  if validator.validate
    logger.error("One or more templates failed to validate")
    exit(1)
  else
    logger.info "Cloudformation templates were validated successfully"
  end
  
  deployer = CfnGuardian::Deploy.new(options,s3.bucket,parameters)
  deployer.upload_templates
  change_set, change_set_type = deployer.create_change_set()
  deployer.wait_for_changeset(change_set.id)
  deployer.execute_change_set(change_set.id)
  deployer.wait_for_execute(change_set_type)
end
disable_alarms() click to toggle source
# File lib/cfnguardian.rb, line 366
def disable_alarms
  set_region(options[:region],true)
  
  alarm_names = CfnGuardian::CloudWatch.get_alarm_names(options[:group],options[:alarm_prefix])
  CfnGuardian::CloudWatch.disable_alarms(alarm_names)
  
  logger.info "Disabled #{alarm_names.length} alarms"
end
enable_alarms() click to toggle source
# File lib/cfnguardian.rb, line 385
def enable_alarms
  set_region(options[:region],true)
  
  alarm_names = CfnGuardian::CloudWatch.get_alarm_names(options[:group],options[:alarm_prefix])
  CfnGuardian::CloudWatch.enable_alarms(alarm_names)
  
  logger.info "#{alarm_names.length} alarms enabled"
end
show_alarms() click to toggle source
# File lib/cfnguardian.rb, line 156
def show_alarms
  set_log_level(options[:debug])
  set_region(options[:region],options[:compare])
  
  if options[:config]
    config_file = options[:config]
  elsif options[:defaults]
    config_file = default_config()
  else
    logger.error('one of `--config YAML` or `--defaults` must be supplied')
    exit -1
  end
  
  compiler = CfnGuardian::Compile.new(config_file)
  compiler.get_resources
  alarms = filter_compiled_alarms(compiler.alarms,options[:filter])

  if alarms.empty?
    logger.error "No matches found" 
    exit 1
  end
  
  headings = ['Property', 'Config']
  formatter = CfnGuardian::DisplayFormatter.new(alarms)
  
  if options[:compare] && !options[:defaults]
    metric_alarms = CfnGuardian::CloudWatch.get_alarms_by_prefix(prefix: 'guardian')
    metric_alarms = CfnGuardian::CloudWatch.filter_alarms(filters: options[:filter], alarms: metric_alarms)

    formatted_alarms = formatter.compare_alarms(metric_alarms)
    headings.push('Deployed')
  else
    formatted_alarms = formatter.alarms()
  end

  if formatted_alarms.any?
    formatted_alarms.each do |fa|
      puts Terminal::Table.new( 
              :title => fa[:title], 
              :headings => headings, 
              :rows => fa[:rows])
    end
  else
    if options[:compare] && !options[:defaults]
      logger.info "No difference found between you config and alarms in deployed AWS"
    else
      logger.warn "No alarms found"
    end
  end
end
show_config_history() click to toggle source
# File lib/cfnguardian.rb, line 305
def show_config_history
  set_region(options[:region],true)

  history = CfnGuardian::CodeCommit.new(options[:repository]).get_commit_history(options[:branch], options[:count])
  if history.any?
    puts Terminal::Table.new(
      :headings => history.first.keys.map{|h| h.to_s.to_heading}, 
      :rows => history.map(&:values))
  end
end
show_drift() click to toggle source
# File lib/cfnguardian.rb, line 125
def show_drift
  set_region(options[:region],true)
  
  rows = []
  drift = CfnGuardian::Drift.new(options[:stack_name])
  nested_stacks = drift.find_nested_stacks
  nested_stacks.each do |stack|
    drift.detect_drift(stack)
    rows << drift.get_drift(stack)
  end
  
  if rows.any?
    puts Terminal::Table.new( 
            :title => "Guardian Alarm Drift".green, 
            :headings => ['Alarm Name', 'Property', 'Expected', 'Actual', 'Type'], 
            :rows => rows.flatten(1))
    exit(1)
  end
end
show_history() click to toggle source
# File lib/cfnguardian.rb, line 260
def show_history
  set_log_level(options[:debug])
  set_region(options[:region],true)
  
  if options[:alarm_names]
    metric_alarms = CfnGuardian::CloudWatch.get_alarms_by_name(alarm_names: options[:alarm_names], state: options[:state])
  else
    metric_alarms = CfnGuardian::CloudWatch.get_alarms_by_prefix(prefix: options[:alarm_prefix], state: options[:state])
  end

  metric_alarms = CfnGuardian::CloudWatch.filter_alarms(filters: options[:filter], alarms: metric_alarms)       
  
  case options[:type]
  when 'state'
    type = 'StateUpdate'
    headings = ['Date', 'Summary', 'Reason']
  when 'config'
    type = 'ConfigurationUpdate'
    headings = ['Date', 'Summary', 'Type']
  end
  
  formatter = CfnGuardian::DisplayFormatter.new()
  
  metric_alarms.each do |alarm|
    history = CfnGuardian::CloudWatch.get_alarm_history(alarm.alarm_name,type)
    rows = formatter.alarm_history(history,type)
    if rows.any?     
      puts Terminal::Table.new( 
              :title => alarm.alarm_name.green, 
              :headings => headings, 
              :rows => rows)
      puts "\n"
    end
  end
end
show_pipeline() click to toggle source
# File lib/cfnguardian.rb, line 323
def show_pipeline
  set_region(options[:region],true)
  pipeline = CfnGuardian::CodePipeline.new(options[:pipeline])
  source = pipeline.get_source()
  build = pipeline.get_build()
  create = pipeline.get_create_changeset()
  deploy = pipeline.get_deploy_changeset()

  puts Terminal::Table.new(
    :title => "Stage: #{source[:stage]}",
    :rows => source[:rows])
    
  puts "\t|"
  puts "\t|"
  
  puts Terminal::Table.new(
    :title => "Stage: #{build[:stage]}",
    :rows => build[:rows])
    
  puts "\t|"
  puts "\t|"
  
  puts Terminal::Table.new(
    :title => "Stage: #{create[:stage]}",
    :rows => create[:rows])
    
  puts "\t|"
  puts "\t|"
  
  puts Terminal::Table.new(
    :title => "Stage: #{deploy[:stage]}",
    :rows => deploy[:rows])
end
show_state() click to toggle source
# File lib/cfnguardian.rb, line 217
def show_state
  set_log_level(options[:debug])
  set_region(options[:region],true)
  action_prefix = nil

  if options[:filter].has_key?('topic')
    action_prefix = get_topic_arn_from_stack(options[:filter]['topic'])
  elsif options[:filter].has_key?('maintenance-group')
    action_prefix = "arn:aws:sns:#{Aws.config[:region]}:#{CfnGuardian::CloudWatch.aws_account_id()}:#{options[:filter]['maintenance-group']}MaintenanceGroup"
  end

  if options[:alarm_names]
    metric_alarms = CfnGuardian::CloudWatch.get_alarms_by_name(alarm_names: options[:alarm_names], state: options[:state], action_prefix: action_prefix)
  else
    metric_alarms = CfnGuardian::CloudWatch.get_alarms_by_prefix(prefix: options[:alarm_prefix], state: options[:state], action_prefix: action_prefix)
  end

  metric_alarms = CfnGuardian::CloudWatch.filter_alarms(filters: options[:filter], alarms: metric_alarms)

  formatter = CfnGuardian::DisplayFormatter.new()
  rows = formatter.alarm_state(metric_alarms)
  
  if rows.any?
    puts Terminal::Table.new( 
          :title => "Alarm State", 
          :headings => ['Alarm Name', 'State', 'Changed', 'Notifications'], 
          :rows => rows)
  else
    logger.warn "No alarms found"
  end
end

Private Instance Methods

default_config() click to toggle source
# File lib/cfnguardian.rb, line 425
def default_config()
  return "#{File.expand_path(File.dirname(__FILE__))}/cfnguardian/config/defaults.yaml"
end
filter_compiled_alarms(alarms,filters) click to toggle source
# File lib/cfnguardian.rb, line 415
def filter_compiled_alarms(alarms,filters)
  filters = filters.slice('group', 'resource', 'alarm', 'topic', 'maintenance-group')
  alarms.select! {|alarm| alarm.group.downcase == filters['group'].downcase} if filters.has_key?('group')
  alarms.select! {|alarm| alarm.resource_id.downcase == filters['resource'].downcase} if filters.has_key?('resource')
  alarms.select! {|alarm| alarm.name.downcase.include? filters['alarm'].downcase} if filters.has_key?('alarm')
  alarms.select! {|alarm| alarm.alarm_action.include? filters['topic']} if filters.has_key?('topic')
  alarms.select! {|alarm| alarm.maintenance_groups.include? "#{filters['maintenance-group']}MaintenanceGroup"} if filters.has_key?('maintenance-group')
  return alarms
end
get_topic_arn_from_stack(topic) click to toggle source
# File lib/cfnguardian.rb, line 429
def get_topic_arn_from_stack(topic)
  client = Aws::CloudFormation::Client.new()
  resp = client.describe_stacks({ stack_name: @stack_name })
  stack = resp.stacks.first
  parameter = stack.parameters.find {|p| p.parameter_key == topic}
  return !parameter.nil? ? parameter.parameter_value : nil
end
set_log_level(debug) click to toggle source
# File lib/cfnguardian.rb, line 411
def set_log_level(debug)
  logger.level = debug ? Logger::DEBUG : Logger::INFO
end
set_region(region,required) click to toggle source
# File lib/cfnguardian.rb, line 396
def set_region(region,required)
  if !region.nil?
    Aws.config.update({region: region})
  elsif !ENV['AWS_REGION'].nil?
    Aws.config.update({region: ENV['AWS_REGION']})
  elsif !ENV['AWS_DEFAULT_REGION'].nil?
    Aws.config.update({region: ENV['AWS_DEFAULT_REGION']})
  else
    if required
      logger.error("No AWS region found. Please suppy the region using option `--region` or setting environment variables `AWS_REGION` `AWS_DEFAULT_REGION`")
      exit(1)
    end
  end
end