class Parser

Command line options parser

Constants

DEFAULT_CONFIG_FILE
DEFAULT_FORMAT
DEFAULT_OUTPUT_FILE
DEFAULT_S3_PATH
DEFAULT_THREADS
MAX_THREADS
Options
SERVICES_CONFIG_FILE

Public Class Methods

parse(options) click to toggle source
# File lib/aws_recon/options.rb, line 33
def self.parse(options)
  begin
    unless (options & ['-h', '--help']).any?
      aws_regions = ['global'].concat(Aws::EC2::Client.new.describe_regions.regions.map(&:region_name))
    end
  rescue Aws::Errors::ServiceError => e
    warn "\nAWS Error: #{e.code}\n\n"
    exit(1)
  end

  aws_services = YAML.load(File.read(SERVICES_CONFIG_FILE), symbolize_names: true)

  args = Options.new(
    aws_regions,
    aws_services.map { |service| service[:name] },
    DEFAULT_CONFIG_FILE,
    DEFAULT_S3_PATH,
    DEFAULT_OUTPUT_FILE,
    DEFAULT_FORMAT,
    DEFAULT_THREADS,
    false,
    false,
    false,
    false,
    false,
    false,
    false,
    false
  )

  opt_parser = OptionParser.new do |opts|
    opts.banner = "\n\x1b[32mAWS Recon\x1b[0m - AWS Inventory Collector (#{AwsRecon::VERSION})\n\nUsage: aws_recon [options]"

    # regions
    opts.on('-r', '--regions [REGIONS]', 'Regions to scan, separated by comma (default: all)') do |regions|
      next if regions.downcase == 'all'

      args.regions = args.regions.filter { |region| regions.split(',').include?(region) }
    end

    # regions to skip
    opts.on('-n', '--not-regions [REGIONS]', 'Regions to skip, separated by comma (default: none)') do |regions|
      next if regions.downcase == 'all'

      args.regions = args.regions.filter { |region| !regions.split(',').include?(region) }
    end

    # services
    opts.on('-s', '--services [SERVICES]', 'Services to scan, separated by comma (default: all)') do |services|
      next if services.downcase == 'all'

      svcs = services.split(',')
      args.services = aws_services.map { |service| service[:name] if svcs.include?(service[:name]) || svcs.include?(service[:alias]) }.compact # rubocop:disable Layout/LineLength
    end

    # services to skip
    opts.on('-x', '--not-services [SERVICES]', 'Services to skip, separated by comma (default: none)') do |services|
      next if services.downcase == 'all'

      svcs = services.split(',')
      args.services = aws_services.map { |service| service[:name] unless svcs.include?(service[:name]) || svcs.include?(service[:alias]) }.compact # rubocop:disable Layout/LineLength
    end

    # config file
    opts.on('-c', '--config [CONFIG]', 'Specify config file for services & regions (e.g. config.yaml)') do |config|
      args.config_file = config
    end

    # write output file to S3 bucket
    opts.on('-b', '--s3-bucket [BUCKET:REGION]', 'Write output file to S3 bucket (default: \'\')') do |bucket_with_region|
      args.stream_output = false
      args.s3 = bucket_with_region
    end

    # output file
    opts.on('-o', '--output [OUTPUT]', 'Specify output file (default: output.json)') do |output|
      args.output_file = File.expand_path(File.join(Dir.pwd, output))
    end

    # output format
    opts.on('-f', '--format [FORMAT]', 'Specify output format (default: aws)') do |f|
      args.output_format = f.downcase if %w[aws custom].include?(f.downcase)
    end

    # threads
    opts.on('-t', '--threads [THREADS]', "Specify max threads (default: #{Parser::DEFAULT_THREADS}, max: 128)") do |threads|
      args.threads = threads.to_i if (0..Parser::MAX_THREADS).include?(threads.to_i)
    end

    # output NDJSON/JSONL format
    opts.on('-l', '--json-lines', 'Output NDJSON/JSONL format (default: false)') do
      args.jsonl = true
    end

    # collect EC2 instance user data
    opts.on('-u', '--user-data', 'Collect EC2 instance user data (default: false)') do
      args.collect_user_data = true
    end

    # skip slow operations
    opts.on('-z', '--skip-slow', 'Skip slow operations (default: false)') do
      args.skip_slow = true
    end

    # skip generating IAM credential report
    opts.on('-g', '--skip-credential-report', 'Skip generating IAM credential report (default: false)') do
      args.skip_credential_report = true
    end

    # stream output (forces JSON lines, doesn't output handled warnings or errors )
    opts.on('-j', '--stream-output', 'Stream JSON lines to stdout (default: false)') do
      args.output_file = nil
      args.verbose = false
      args.debug = false
      args.stream_output = true
    end

    # verbose
    opts.on('-v', '--verbose', 'Output client progress and current operation') do
      args.verbose = true unless args.stream_output
    end

    # re-raise exceptions
    opts.on('-q', '--quit-on-exception', 'Stop collection if an API error is encountered (default: false)') do
      args.quit_on_exception = true
    end

    # debug
    opts.on('-d', '--debug', 'Output debug with wire trace info') do
      unless args.stream_output
        args.debug = true
        args.verbose = true
      end
    end

    opts.on('-h', '--help', 'Print this help information') do
      puts opts
      exit
    end
  end

  opt_parser.parse!(options)
  args
end