Class: Iggy::Terraform
- Inherits:
-
Object
- Object
- Iggy::Terraform
- Defined in:
- lib/inspec-iggy/terraform.rb
Constant Summary collapse
- TAG_NAME =
makes it easier to change out later
"iggy_name_"- TAG_URL =
"iggy_url_"
Class Method Summary collapse
-
.parse_extract(file) ⇒ Object
parse through the JSON for the tagged Resources.
-
.parse_generate(file) ⇒ Object
parse through the JSON and generate InSpec controls.
-
.parse_tfstate(file) ⇒ Object
boilerplate tfstate parsing.
Class Method Details
.parse_extract(file) ⇒ Object
parse through the JSON for the tagged Resources
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/inspec-iggy/terraform.rb', line 37 def self.parse_extract(file) tfstate = parse_tfstate(file) # InSpec profiles extracted extracted_profiles = {} # iterate over the resources tf_resources = tfstate["modules"][0]["resources"] tf_resources.keys.each do |tf_res| tf_res_id = tf_resources[tf_res]["primary"]["id"] # get the attributes, see if any of them have a tagged profile attached tf_resources[tf_res]["primary"]["attributes"].keys.each do |attr| next unless attr.start_with?("tags." + TAG_NAME) Inspec::Log.debug "Iggy::Terraform.parse_extract tf_res = #{tf_res} attr = #{attr} MATCHED TAG" # get the URL and the name of the profiles name = attr.split(TAG_NAME)[1] url = tf_resources[tf_res]["primary"]["attributes"]["tags.#{TAG_URL}#{name}"] if tf_res.start_with?("aws_vpc") # should this be VPC or subnet? # if it's a VPC, store it as the VPC id + name key = tf_res_id + ":" + name Inspec::Log.debug "Iggy::Terraform.parse_extract aws_vpc tagged with InSpec #{key}" extracted_profiles[key] = { "type" => "aws_vpc", "az" => "us-west-2", "url" => url, } elsif tf_res.start_with?("aws_instance") # if it's a node, get information about the IP and SSH/WinRM key = tf_res_id + ":" + name Inspec::Log.debug "Iggy::Terraform.parse_extract aws_instance tagged with InSpec #{key}" extracted_profiles[key] = { "type" => "aws_instance", "public_ip" => tf_resources[tf_res]["primary"]["attributes"]["public_ip"], "key_name" => tf_resources[tf_res]["primary"]["attributes"]["key_name"], "url" => url, } else # should generic AWS just be the default except for instances? STDERR.puts "ERROR: #{file} #{tf_res_id} has an InSpec-tagged resource but #{tf_res} is currently unsupported." exit(-1) end end end Inspec::Log.debug "Iggy::Terraform.parse_extract extracted_profiles = #{extracted_profiles}" extracted_profiles end |
.parse_generate(file) ⇒ Object
parse through the JSON and generate InSpec controls
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/inspec-iggy/terraform.rb', line 85 def self.parse_generate(file) tfstate = parse_tfstate(file) basename = File.basename(file) absolutename = File.absolute_path(file) # InSpec controls generated generated_controls = [] # iterate over the resources tf_resources = tfstate["modules"][0]["resources"] tf_resources.keys.each do |tf_res| tf_res_type = tf_resources[tf_res]["type"] # add translation layer if InspecHelper::TERRAFORM_RESOURCES.keys.include?(tf_res_type) Inspec::Log.debug "Iggy::Terraform.parse_generate tf_res_type = #{tf_res_type} #{InspecHelper::TERRAFORM_RESOURCES[tf_res_type]} TRANSLATED" tf_res_type = InspecHelper::TERRAFORM_RESOURCES[tf_res_type] end # does this match an InSpec resource? if InspecHelper::RESOURCES.include?(tf_res_type) Inspec::Log.debug "Iggy::Terraform.parse_generate tf_res_type = #{tf_res_type} MATCH" tf_res_id = tf_resources[tf_res]["primary"]["id"] # insert new control based off the resource's ID ctrl = Inspec::Control.new ctrl.id = "#{tf_res_type}::#{tf_res_id}" ctrl.title = "Iggy #{basename} #{tf_res_type}::#{tf_res_id}" ctrl.desc = "#{tf_res_type}::#{tf_res_id} from the source file #{absolutename}\nGenerated by Iggy v#{Iggy::VERSION}" ctrl.impact = "1.0" describe = Inspec::Describe.new # describes the resourde with the id as argument describe.qualifier.push([tf_res_type, tf_res_id]) # ensure the resource exists describe.add_test(nil, "exist", nil) # if there's a match, see if there are matching InSpec properties inspec_properties = Iggy::InspecHelper.resource_properties(tf_res_type) tf_resources[tf_res]["primary"]["attributes"].keys.each do |attr| if inspec_properties.member?(attr) Inspec::Log.debug "Iggy::Terraform.parse_generate #{tf_res_type} inspec_property = #{attr} MATCH" value = tf_resources[tf_res]["primary"]["attributes"][attr] describe.add_test(attr, "cmp", value) else Inspec::Log.debug "Iggy::Terraform.parse_generate #{tf_res_type} inspec_property = #{attr} SKIP" end end ctrl.add_test(describe) generated_controls.push(ctrl) else Inspec::Log.debug "Iggy::Terraform.parse_generate tf_res_type = #{tf_res_type} SKIP" end end Inspec::Log.debug "Iggy::Terraform.parse_generate generated_controls = #{generated_controls}" generated_controls end |
.parse_tfstate(file) ⇒ Object
boilerplate tfstate parsing
21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/inspec-iggy/terraform.rb', line 21 def self.parse_tfstate(file) Inspec::Log.debug "Iggy::Terraform.parse_tfstate file = #{file}" begin unless File.file?(file) STDERR.puts "ERROR: #{file} is an invalid file, please check your path." exit(-1) end tfstate = JSON.parse(File.read(file)) rescue JSON::ParserError => e STDERR.puts e. STDERR.puts "ERROR: Parsing error in #{file}." exit(-1) end end |