Repository: tenable/terrascan (language: python)

Issue:
terrascan: scanning terraform files vs terraform plan
Actually this is not a bug report it is a feature request.

Terrascan downloads remote source in terraform modules and evaluate the variables while checking terraform codes.
some other iac tools(regula,checkov)  and OPA itself can scan terraform plan output in JSON.

Do you have any plan to support terraform plan output in JSON or do you think that scanning terraform plan provides better result?
From checkov documentation: https://github.com/bridgecrewio/checkov/blob/master/docs/2.Concepts/Evaluate%20Terraform%20Plan.md#evaluate-checkov-policies-on-terraform-plan

# Evaluate Checkov policies on Terraform plan

Checkov supports the evaluation of policies on resources declared in `.tf` files. It can also be used to evaluate `terraform plan` expressed in a json file. 
Plan evaluation provides Checkov additional dependencies and context that can result in a more complete scan result.
Since Terraform plan files may contain arguments (like secrets) that are injected dynamically, it is advised to run a plan evaluation using Checkov in a secure CI/CD pipeline setting.



Discussion / hints:
Hi @ismailyenigul! 
We do plan to support plan based scanning! Will follow up with timelines
Thanks @amirbenv 
Happy to test it when it is implemented.  Is there an estimated time frame for this feature?
Not yet, but should be in the next few weeks. It would be good to know how popular this feature is- If others are interested let us know by commenting on this thread!
I would like to have this feature.
I would like to have this feature too.

Fix patch:
diff --git a/go.mod b/go.mod
index c76b2a77c..b602e9660 100644
--- a/go.mod
+++ b/go.mod
@@ -18,6 +18,7 @@ require (
 	github.com/hashicorp/hcl/v2 v2.8.2
 	github.com/hashicorp/terraform v0.14.4
 	github.com/iancoleman/strcase v0.1.3
+	github.com/itchyny/gojq v0.12.1
 	github.com/mattn/go-isatty v0.0.12
 	github.com/mitchellh/go-homedir v1.1.0
 	github.com/onsi/ginkgo v1.12.1
@@ -32,7 +33,7 @@ require (
 	golang.org/x/mod v0.4.1 // indirect
 	golang.org/x/sys v0.0.0-20210218155724-8ebf48af031b
 	gopkg.in/src-d/go-git.v4 v4.13.1
-	gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
+	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
 	helm.sh/helm/v3 v3.4.0
 	honnef.co/go/tools v0.1.1 // indirect
 	sigs.k8s.io/kustomize/api v0.7.2
diff --git a/go.sum b/go.sum
index c5894914d..3a7ed5738 100644
--- a/go.sum
+++ b/go.sum
@@ -598,6 +598,13 @@ github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
 github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
+github.com/itchyny/astgen-go v0.0.0-20210113000433-0da0671862a3 h1:l7vogWrq+zj8v5t/G69/eT13nAGs2H7cq+CI2nlnKdk=
+github.com/itchyny/astgen-go v0.0.0-20210113000433-0da0671862a3/go.mod h1:296z3W7Xsrp2mlIY88ruDKscuvrkL6zXCNRtaYVshzw=
+github.com/itchyny/go-flags v1.5.0/go.mod h1:lenkYuCobuxLBAd/HGFE4LRoW8D3B6iXRQfWYJ+MNbA=
+github.com/itchyny/gojq v0.12.1 h1:pQJrG8LXgEbZe9hvpfjKg7UlBfieQQydIw3YQq+7WIA=
+github.com/itchyny/gojq v0.12.1/go.mod h1:Y5Lz0qoT54ii+ucY/K3yNDy19qzxZvWNBMBpKUDQR/4=
+github.com/itchyny/timefmt-go v0.1.1 h1:rLpnm9xxb39PEEVzO0n4IRp0q6/RmBc7Dy/rE4HrA0U=
+github.com/itchyny/timefmt-go v0.1.1/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A=
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
@@ -697,6 +704,7 @@ github.com/mattn/go-oci8 v0.0.7/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mN
 github.com/mattn/go-runewidth v0.0.0-20181025052659-b20a3daf6a39/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/mattn/go-shellwords v1.0.4/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
 github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
 github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
@@ -1238,6 +1246,7 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
 golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210218155724-8ebf48af031b h1:lAZ0/chPUDWwjqosYR0X4M490zQhMsiJ4K3DbA7o+3g=
@@ -1317,7 +1326,6 @@ golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWc
 golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200609164405-eb789aa7ce50/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
@@ -1325,8 +1333,6 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc
 golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
 golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20210115202250-e0d201561e39 h1:BTs2GMGSMWpgtCpv1CE7vkJTv7XcHdcLLnAMu7UbgTY=
-golang.org/x/tools v0.0.0-20210115202250-e0d201561e39/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1471,8 +1477,8 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
-gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
 gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
 helm.sh/helm/v3 v3.4.0 h1:rsut6hqQfjD3G/ic1XPh3KyasfOHZuDUhtyAJjuquew=
@@ -1485,8 +1491,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-honnef.co/go/tools v0.1.0 h1:AWNL1W1i7f0wNZ8VwOKNJ0sliKvOF/adn0EHenfUh+c=
-honnef.co/go/tools v0.1.0/go.mod h1:XtegFAyX/PfluP4921rXU5IkjkqBCDnUq4W8VCIoKvM=
 honnef.co/go/tools v0.1.1 h1:EVDuO03OCZwpV2t/tLLxPmPiomagMoBOgfPt0FM+4IY=
 honnef.co/go/tools v0.1.1/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
 k8s.io/api v0.19.0 h1:XyrFIJqTYZJ2DU7FBE/bSPz7b1HvbVBuBf07oeo6eTc=
diff --git a/pkg/iac-providers/tfplan.go b/pkg/iac-providers/tfplan.go
new file mode 100644
index 000000000..d316d48c3
--- /dev/null
+++ b/pkg/iac-providers/tfplan.go
@@ -0,0 +1,36 @@
+/*
+    Copyright (C) 2020 Accurics, Inc.
+
+	Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+		http://www.apache.org/licenses/LICENSE-2.0
+
+	Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+package iacprovider
+
+import (
+	"reflect"
+
+	tfplanv1 "github.com/accurics/terrascan/pkg/iac-providers/tfplan/v1"
+)
+
+// tfplan specific constants
+const (
+	tfplan                  supportedIacType    = "tfplan"
+	tfplanV1                supportedIacVersion = "v1"
+	tfplanDefaultIacVersion                     = tfplanV1
+)
+
+// register tfplan as an IaC provider with terrascan
+func init() {
+	// register iac provider
+	RegisterIacProvider(tfplan, tfplanV1, tfplanDefaultIacVersion, reflect.TypeOf(tfplanv1.TFPlan{}))
+}
diff --git a/pkg/iac-providers/tfplan/v1/load-dir.go b/pkg/iac-providers/tfplan/v1/load-dir.go
new file mode 100644
index 000000000..4ae8b1259
--- /dev/null
+++ b/pkg/iac-providers/tfplan/v1/load-dir.go
@@ -0,0 +1,33 @@
+/*
+    Copyright (C) 2020 Accurics, Inc.
+
+	Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+		http://www.apache.org/licenses/LICENSE-2.0
+
+	Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+package tfplan
+
+import (
+	"fmt"
+
+	"github.com/accurics/terrascan/pkg/iac-providers/output"
+)
+
+var (
+	errIacDirNotSupport = fmt.Errorf("tfplan should always be a file, not a directory. Please specify path to tfplan file  with '-f' option")
+)
+
+// LoadIacDir is not supported for tfplan IacType. Terraform plan should always
+// be a file and not a directory
+func (k *TFPlan) LoadIacDir(absRootDir string) (output.AllResourceConfigs, error) {
+	return output.AllResourceConfigs{}, errIacDirNotSupport
+}
diff --git a/pkg/iac-providers/tfplan/v1/load-file.go b/pkg/iac-providers/tfplan/v1/load-file.go
new file mode 100644
index 000000000..e19f92810
--- /dev/null
+++ b/pkg/iac-providers/tfplan/v1/load-file.go
@@ -0,0 +1,117 @@
+/*
+    Copyright (C) 2020 Accurics, Inc.
+
+	Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+		http://www.apache.org/licenses/LICENSE-2.0
+
+	Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+package tfplan
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"strings"
+
+	"github.com/accurics/terrascan/pkg/iac-providers/output"
+	"github.com/accurics/terrascan/pkg/utils"
+	"go.uber.org/zap"
+)
+
+const (
+	jqQuery             = `[.planned_values.root_module | .. | select(.type? != null and .address? != null and .mode? == "managed") | {id: .address?, type: .type?, name: .name?, config: .values?, source: ""}]`
+	tfPlanFormatVersion = "0.1"
+)
+
+var (
+	errIncorrectFormatVersion = fmt.Errorf("terraform format version shoule be '%s'", tfPlanFormatVersion)
+	errEmptyTerraformVersion  = fmt.Errorf("terraform version cannot be empty in tfplan json")
+)
+
+// LoadIacFile parses the given tfplan file from the given file path
+func (t *TFPlan) LoadIacFile(absFilePath string) (allResourcesConfig output.AllResourceConfigs, err error) {
+
+	zap.S().Debug("processing tfplan file")
+
+	// read tfplan json file
+	tfjson, err := ioutil.ReadFile(absFilePath)
+	if err != nil {
+		errMsg := fmt.Sprintf("failed to read tfplan JSON file. error: '%v'", err)
+		zap.S().Debug(errMsg)
+		return allResourcesConfig, fmt.Errorf(errMsg)
+	}
+
+	// validate if provide file is a valid tfplan file
+	if err := t.isValidTFPlanJSON(tfjson); err != nil {
+		return allResourcesConfig, fmt.Errorf("invalid terraform json file; error: '%v'", err)
+	}
+
+	// run jq query on tfplan json
+	processed, err := utils.JQFilterWithQuery(jqQuery, tfjson)
+	if err != nil {
+		errMsg := fmt.Sprintf("failed to process tfplan JSON. error: '%v'", err)
+		zap.S().Debug(errMsg)
+		return allResourcesConfig, fmt.Errorf(errMsg)
+	}
+
+	// decode processed out into output.ResourceConfig
+	var resourceConfigs []output.ResourceConfig
+	if err := json.Unmarshal(processed, &resourceConfigs); err != nil {
+		errMsg := fmt.Sprintf("failed to decode proceesed jq output. error: '%v'", err)
+		zap.S().Debug(errMsg)
+		return allResourcesConfig, fmt.Errorf(errMsg)
+	}
+
+	// create AllResourceConfigs from resourceConfigs
+	allResourcesConfig = make(map[string][]output.ResourceConfig)
+	for _, r := range resourceConfigs {
+		r.ID = getTFID(r.ID)
+		if _, present := allResourcesConfig[r.Type]; !present {
+			allResourcesConfig[r.Type] = []output.ResourceConfig{r}
+		} else {
+			allResourcesConfig[r.Type] = append(allResourcesConfig[r.Type], r)
+		}
+	}
+
+	// return output
+	return allResourcesConfig, nil
+}
+
+// isValidTFPlanJSON validates whether the provided file is a valid tf json file
+func (t *TFPlan) isValidTFPlanJSON(tfjson []byte) error {
+
+	// decode tfjson into map[string]interface{}
+	if err := json.Unmarshal(tfjson, &t); err != nil {
+		return fmt.Errorf("failed to decode tfplan json. error: '%v'", err)
+	}
+
+	// check format version
+	if t.FormatVersion != tfPlanFormatVersion {
+		return errIncorrectFormatVersion
+	}
+
+	// check terraform version
+	if t.TerraformVersion == "" {
+		return errEmptyTerraformVersion
+	}
+
+	return nil
+}
+
+// getTFID returns a valid resource ID for terraform
+func getTFID(id string) string {
+	split := strings.Split(id, ".")
+	if len(split) <= 2 {
+		return strings.Join(split, ".")
+	}
+	return strings.Join(split[len(split)-2:], ".")
+}
diff --git a/pkg/iac-providers/tfplan/v1/types.go b/pkg/iac-providers/tfplan/v1/types.go
new file mode 100644
index 000000000..8aaacfe5e
--- /dev/null
+++ b/pkg/iac-providers/tfplan/v1/types.go
@@ -0,0 +1,23 @@
+/*
+    Copyright (C) 2020 Accurics, Inc.
+
+	Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+		http://www.apache.org/licenses/LICENSE-2.0
+
+	Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+package tfplan
+
+// TFPlan implements the IacProvider interface
+type TFPlan struct {
+	FormatVersion    string `json:"format_version"`
+	TerraformVersion string `json:"terraform_version"`
+}
diff --git a/pkg/utils/jqhelper.go b/pkg/utils/jqhelper.go
new file mode 100644
index 000000000..239ac55a9
--- /dev/null
+++ b/pkg/utils/jqhelper.go
@@ -0,0 +1,69 @@
+/*
+    Copyright (C) 2020 Accurics, Inc.
+
+	Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+		http://www.apache.org/licenses/LICENSE-2.0
+
+	Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+package utils
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"time"
+
+	"github.com/itchyny/gojq"
+	"go.uber.org/zap"
+)
+
+// JQFilterWithQuery runs jq query on the given input and returns the output
+func JQFilterWithQuery(jqQuery string, jsonInput []byte) ([]byte, error) {
+
+	var processed []byte
+
+	// convert read json input into map[string]interface{}
+	var input map[string]interface{}
+	if err := json.Unmarshal(jsonInput, &input); err != nil {
+		return processed, fmt.Errorf("failed to decode input JSON. error: '%v'", err)
+	}
+
+	// parse jq query
+	query, err := gojq.Parse(jqQuery)
+	if err != nil {
+		return processed, fmt.Errorf("failed to parse jq query. error: '%v'", err)
+	}
+
+	// run jq query on input
+	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
+	defer cancel()
+	iter := query.RunWithContext(ctx, input)
+	for {
+		v, ok := iter.Next()
+		if !ok {
+			break
+		}
+		if err, ok := v.(error); ok {
+			zap.S().Warn("error in processing jq query; error: '%v'", err)
+			continue
+		}
+
+		jqout, err := json.Marshal(v)
+		if err != nil {
+			zap.S().Warn("failed to encode jq output into JSON. error: '%v'", err)
+			continue
+		}
+		processed = append(processed, jqout...)
+	}
+
+	return processed, nil
+}


Test patch:
diff --git a/pkg/iac-providers/tfplan/v1/load-dir_test.go b/pkg/iac-providers/tfplan/v1/load-dir_test.go
new file mode 100644
index 000000000..b3d575c3e
--- /dev/null
+++ b/pkg/iac-providers/tfplan/v1/load-dir_test.go
@@ -0,0 +1,37 @@
+/*
+    Copyright (C) 2020 Accurics, Inc.
+
+	Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+		http://www.apache.org/licenses/LICENSE-2.0
+
+	Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+package tfplan
+
+import (
+	"reflect"
+	"testing"
+)
+
+func TestLoadIacDir(t *testing.T) {
+
+	t.Run("directory not supported", func(t *testing.T) {
+		var (
+			dirPath = "some-path"
+			tfplan  = TFPlan{}
+			wantErr = errIacDirNotSupport
+		)
+		_, err := tfplan.LoadIacDir(dirPath)
+		if !reflect.DeepEqual(wantErr, err) {
+			t.Errorf("error want: '%v', got: '%v'", wantErr, err)
+		}
+	})
+}
diff --git a/pkg/iac-providers/tfplan/v1/load-file_test.go b/pkg/iac-providers/tfplan/v1/load-file_test.go
new file mode 100644
index 000000000..4172ad108
--- /dev/null
+++ b/pkg/iac-providers/tfplan/v1/load-file_test.go
@@ -0,0 +1,173 @@
+/*
+    Copyright (C) 2020 Accurics, Inc.
+
+	Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+		http://www.apache.org/licenses/LICENSE-2.0
+
+	Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+package tfplan
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"reflect"
+	"testing"
+
+	"github.com/accurics/terrascan/pkg/iac-providers/output"
+)
+
+func TestLoadIacFile(t *testing.T) {
+
+	table := []struct {
+		name     string
+		filePath string
+		tfplan   TFPlan
+		want     output.AllResourceConfigs
+		wantErr  error
+	}{
+		{
+			name:     "invalid filepath",
+			filePath: "not-there",
+			tfplan:   TFPlan{},
+			wantErr:  fmt.Errorf("failed to read tfplan JSON file. error: 'open not-there: no such file or directory'"),
+		},
+		{
+			name:     "invalid json",
+			filePath: "./testdata/invalid-json.json",
+			tfplan:   TFPlan{},
+			wantErr:  fmt.Errorf("invalid terraform json file; error: 'failed to decode tfplan json. error: 'invalid character 'I' looking for beginning of value''"),
+		},
+		{
+			name:     "invalid tfplan json",
+			filePath: "./testdata/invalid-tfplan.json",
+			tfplan:   TFPlan{},
+			wantErr:  fmt.Errorf("invalid terraform json file; error: 'terraform format version shoule be '0.1''"),
+		},
+		{
+			name:     "valid tfplan json",
+			filePath: "./testdata/valid-tfplan.json",
+			tfplan:   TFPlan{},
+			wantErr:  nil,
+		},
+	}
+
+	for _, tt := range table {
+		t.Run(tt.name, func(t *testing.T) {
+			_, gotErr := tt.tfplan.LoadIacFile(tt.filePath)
+			if !reflect.DeepEqual(gotErr, tt.wantErr) {
+				t.Errorf("unexpected error; gotErr: '%v', wantErr: '%v'", gotErr, tt.wantErr)
+			}
+		})
+	}
+
+	t.Run("validate tfplan iac output", func(t *testing.T) {
+		var (
+			tfplan             = TFPlan{}
+			tfplanFile         = "./testdata/valid-tfplan.json"
+			tfplanOutput       = "./testdata/valid-tfplan-resource-config.json"
+			wantErr      error = nil
+		)
+
+		got, err := tfplan.LoadIacFile(tfplanFile)
+		if !reflect.DeepEqual(err, wantErr) {
+			t.Errorf("error want: '%v', got: '%v'", wantErr, err)
+		}
+
+		gotBytes, _ := json.MarshalIndent(got, "", "  ")
+		gotBytes = append(gotBytes, []byte{'\n'}...)
+		wantBytes, _ := ioutil.ReadFile(tfplanOutput)
+		if !reflect.DeepEqual(bytes.TrimSpace(gotBytes), bytes.TrimSpace(wantBytes)) {
+			t.Errorf("unexpected error; got '%v', want: '%v'", string(gotBytes), string(wantBytes))
+		}
+	})
+}
+
+func TestIsValidTFPlanJSON(t *testing.T) {
+
+	tfplan := TFPlan{}
+
+	table := []struct {
+		name    string
+		tfjson  []byte
+		wantErr error
+	}{
+		{
+			name:    "invalid json",
+			tfjson:  []byte("I am invalid"),
+			wantErr: fmt.Errorf("failed to decode tfplan json. error: 'invalid character 'I' looking for beginning of value'"),
+		},
+		{
+			name:    "incorrect terraform format version",
+			tfjson:  []byte(`{"format_version": "bad version"}`),
+			wantErr: errIncorrectFormatVersion,
+		},
+		{
+			name:    "empty terraform version",
+			tfjson:  []byte(`{"format_version": "0.1"}`),
+			wantErr: errEmptyTerraformVersion,
+		},
+		{
+			name:    "valid tfplan json",
+			tfjson:  []byte(`{"format_version": "0.1", "terraform_version": "0.12.3"}`),
+			wantErr: nil,
+		},
+	}
+
+	for _, tt := range table {
+		t.Run(tt.name, func(t *testing.T) {
+			err := tfplan.isValidTFPlanJSON(tt.tfjson)
+			if !reflect.DeepEqual(err, tt.wantErr) {
+				t.Errorf("error got: '%v', want: '%v'", err, tt.wantErr)
+			}
+		})
+	}
+}
+
+func TestGetTFID(t *testing.T) {
+
+	table := []struct {
+		name  string
+		input string
+		want  string
+	}{
+		{
+			name:  "empty input",
+			input: "",
+			want:  "",
+		},
+		{
+			name:  "regular terraform id",
+			input: "x.y",
+			want:  "x.y",
+		},
+		{
+			name:  "long terraform id",
+			input: "x.y.z",
+			want:  "y.z",
+		},
+		{
+			name:  "extra long terraform id",
+			input: "w.x.y.z",
+			want:  "y.z",
+		},
+	}
+
+	for _, tt := range table {
+		got := getTFID(tt.input)
+		if got != tt.want {
+			t.Errorf("got: '%v', want: '%v'", got, tt.want)
+		}
+	}
+
+}
diff --git a/pkg/iac-providers/tfplan/v1/testdata/invalid-json.json b/pkg/iac-providers/tfplan/v1/testdata/invalid-json.json
new file mode 100644
index 000000000..70f20a4fb
--- /dev/null
+++ b/pkg/iac-providers/tfplan/v1/testdata/invalid-json.json
@@ -0,0 +1,1 @@
+I am an invalid json
diff --git a/pkg/iac-providers/tfplan/v1/testdata/invalid-tfplan.json b/pkg/iac-providers/tfplan/v1/testdata/invalid-tfplan.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/pkg/iac-providers/tfplan/v1/testdata/invalid-tfplan.json
@@ -0,0 +1,1 @@
+{}
diff --git a/pkg/iac-providers/tfplan/v1/testdata/valid-tfplan-resource-config.json b/pkg/iac-providers/tfplan/v1/testdata/valid-tfplan-resource-config.json
new file mode 100644
index 000000000..fb2ffa3b8
--- /dev/null
+++ b/pkg/iac-providers/tfplan/v1/testdata/valid-tfplan-resource-config.json
@@ -0,0 +1,64 @@
+{
+  "aws_s3_bucket": [
+    {
+      "id": "aws_s3_bucket.demo-example",
+      "name": "demo-example",
+      "source": "",
+      "line": 0,
+      "type": "aws_s3_bucket",
+      "config": {
+        "acl": "private",
+        "bucket": "demoexample-1",
+        "bucket_prefix": null,
+        "cors_rule": [],
+        "force_destroy": false,
+        "grant": [],
+        "lifecycle_rule": [],
+        "logging": [],
+        "object_lock_configuration": [],
+        "policy": null,
+        "replication_configuration": [],
+        "server_side_encryption_configuration": [],
+        "tags": null,
+        "versioning": [
+          {
+            "enabled": false,
+            "mfa_delete": false
+          }
+        ],
+        "website": []
+      },
+      "skip_rules": null
+    },
+    {
+      "id": "aws_s3_bucket.demo-s3",
+      "name": "demo-s3",
+      "source": "",
+      "line": 0,
+      "type": "aws_s3_bucket",
+      "config": {
+        "acl": "private",
+        "bucket": "sample_prefix_test20-terraformcloud",
+        "bucket_prefix": null,
+        "cors_rule": [],
+        "force_destroy": false,
+        "grant": [],
+        "lifecycle_rule": [],
+        "logging": [],
+        "object_lock_configuration": [],
+        "policy": null,
+        "replication_configuration": [],
+        "server_side_encryption_configuration": [],
+        "tags": null,
+        "versioning": [
+          {
+            "enabled": false,
+            "mfa_delete": false
+          }
+        ],
+        "website": []
+      },
+      "skip_rules": null
+    }
+  ]
+}
diff --git a/pkg/iac-providers/tfplan/v1/testdata/valid-tfplan.json b/pkg/iac-providers/tfplan/v1/testdata/valid-tfplan.json
new file mode 100644
index 000000000..151b3f537
--- /dev/null
+++ b/pkg/iac-providers/tfplan/v1/testdata/valid-tfplan.json
@@ -0,0 +1,1 @@
+{"format_version":"0.1","terraform_version":"0.13.5","variables":{"s3_bucket_prefix":{"value":"sample_prefix_test20"}},"planned_values":{"root_module":{"resources":[{"address":"aws_s3_bucket.demo-example","mode":"managed","type":"aws_s3_bucket","name":"demo-example","provider_name":"registry.terraform.io/hashicorp/aws","schema_version":0,"values":{"acl":"private","bucket":"demoexample-1","bucket_prefix":null,"cors_rule":[],"force_destroy":false,"grant":[],"lifecycle_rule":[],"logging":[],"object_lock_configuration":[],"policy":null,"replication_configuration":[],"server_side_encryption_configuration":[],"tags":null,"versioning":[{"enabled":false,"mfa_delete":false}],"website":[]}},{"address":"aws_s3_bucket.demo-s3","mode":"managed","type":"aws_s3_bucket","name":"demo-s3","provider_name":"registry.terraform.io/hashicorp/aws","schema_version":0,"values":{"acl":"private","bucket":"sample_prefix_test20-terraformcloud","bucket_prefix":null,"cors_rule":[],"force_destroy":false,"grant":[],"lifecycle_rule":[],"logging":[],"object_lock_configuration":[],"policy":null,"replication_configuration":[],"server_side_encryption_configuration":[],"tags":null,"versioning":[{"enabled":false,"mfa_delete":false}],"website":[]}}]}},"resource_changes":[{"address":"aws_s3_bucket.demo-example","mode":"managed","type":"aws_s3_bucket","name":"demo-example","provider_name":"registry.terraform.io/hashicorp/aws","change":{"actions":["create"],"before":null,"after":{"acl":"private","bucket":"demoexample-1","bucket_prefix":null,"cors_rule":[],"force_destroy":false,"grant":[],"lifecycle_rule":[],"logging":[],"object_lock_configuration":[],"policy":null,"replication_configuration":[],"server_side_encryption_configuration":[],"tags":null,"versioning":[{"enabled":false,"mfa_delete":false}],"website":[]},"after_unknown":{"acceleration_status":true,"arn":true,"bucket_domain_name":true,"bucket_regional_domain_name":true,"cors_rule":[],"grant":[],"hosted_zone_id":true,"id":true,"lifecycle_rule":[],"logging":[],"object_lock_configuration":[],"region":true,"replication_configuration":[],"request_payer":true,"server_side_encryption_configuration":[],"versioning":[{}],"website":[],"website_domain":true,"website_endpoint":true}}},{"address":"aws_s3_bucket.demo-s3","mode":"managed","type":"aws_s3_bucket","name":"demo-s3","provider_name":"registry.terraform.io/hashicorp/aws","change":{"actions":["create"],"before":null,"after":{"acl":"private","bucket":"sample_prefix_test20-terraformcloud","bucket_prefix":null,"cors_rule":[],"force_destroy":false,"grant":[],"lifecycle_rule":[],"logging":[],"object_lock_configuration":[],"policy":null,"replication_configuration":[],"server_side_encryption_configuration":[],"tags":null,"versioning":[{"enabled":false,"mfa_delete":false}],"website":[]},"after_unknown":{"acceleration_status":true,"arn":true,"bucket_domain_name":true,"bucket_regional_domain_name":true,"cors_rule":[],"grant":[],"hosted_zone_id":true,"id":true,"lifecycle_rule":[],"logging":[],"object_lock_configuration":[],"region":true,"replication_configuration":[],"request_payer":true,"server_side_encryption_configuration":[],"versioning":[{}],"website":[],"website_domain":true,"website_endpoint":true}}}],"configuration":{"provider_config":{"aws":{"name":"aws","expressions":{"region":{"constant_value":"us-east-1"}}}},"root_module":{"resources":[{"address":"aws_s3_bucket.demo-example","mode":"managed","type":"aws_s3_bucket","name":"demo-example","provider_config_key":"aws","expressions":{"bucket":{"constant_value":"demoexample-1"},"versioning":[{"enabled":{"constant_value":false},"mfa_delete":{"constant_value":false}}]},"schema_version":0},{"address":"aws_s3_bucket.demo-s3","mode":"managed","type":"aws_s3_bucket","name":"demo-s3","provider_config_key":"aws","expressions":{"bucket":{"references":["var.s3_bucket_prefix"]},"versioning":[{"enabled":{"constant_value":false},"mfa_delete":{"constant_value":false}}]},"schema_version":0}],"variables":{"s3_bucket_prefix":{"default":"sample_prefix_test20"}}}}}
diff --git a/pkg/utils/jqhelper_test.go b/pkg/utils/jqhelper_test.go
new file mode 100644
index 000000000..4860d2005
--- /dev/null
+++ b/pkg/utils/jqhelper_test.go
@@ -0,0 +1,77 @@
+/*
+    Copyright (C) 2020 Accurics, Inc.
+
+	Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+		http://www.apache.org/licenses/LICENSE-2.0
+
+	Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+package utils
+
+import (
+	"fmt"
+	"reflect"
+	"testing"
+)
+
+func TestJQFilterWithQuery(t *testing.T) {
+
+	table := []struct {
+		name    string
+		jqQuery string
+		input   []byte
+		want    []byte
+		wantErr error
+	}{
+		{
+			name:    "invalid json",
+			jqQuery: "",
+			input:   []byte{},
+			want:    []byte{},
+			wantErr: fmt.Errorf("failed to decode input JSON. error: 'unexpected end of JSON input'"),
+		},
+		{
+			name:    "invalid query",
+			jqQuery: "am invalid",
+			input:   []byte("{}"),
+			want:    []byte(""),
+			wantErr: fmt.Errorf("failed to parse jq query. error: 'unexpected token \"invalid\"'"),
+		},
+		{
+			name:    "jq error",
+			jqQuery: "def f: f; f, f",
+			input:   []byte("{}"),
+			want:    []byte(""),
+			wantErr: nil,
+		},
+		{
+			name:    "simple query",
+			jqQuery: ".foo",
+			input:   []byte("{\"foo\": 128}"),
+			want:    []byte("128"),
+			wantErr: nil,
+		},
+	}
+
+	for _, tt := range table {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := JQFilterWithQuery(tt.jqQuery, tt.input)
+
+			if string(got) != string(tt.want) {
+				t.Errorf("got: '%v', want: '%v'", string(got), string(tt.want))
+			}
+
+			if !reflect.DeepEqual(err, tt.wantErr) {
+				t.Errorf("error got: '%v', want: '%v'", err, tt.wantErr)
+			}
+		})
+	}
+}
diff --git a/test/e2e/help/golden/help_scan.txt b/test/e2e/help/golden/help_scan.txt
index 9c1f1c053..3e7a251f8 100644
--- a/test/e2e/help/golden/help_scan.txt
+++ b/test/e2e/help/golden/help_scan.txt
@@ -10,8 +10,8 @@ Flags:
   -h, --help                      help for scan
   -d, --iac-dir string            path to a directory containing one or more IaC files (default ".")
   -f, --iac-file string           path to a single IaC file
-  -i, --iac-type string           iac type (helm, k8s, kustomize, terraform)
-      --iac-version string        iac version (helm: v3, k8s: v1, kustomize: v3, terraform: v12, v13, v14)
+  -i, --iac-type string           iac type (helm, k8s, kustomize, terraform, tfplan)
+      --iac-version string        iac version (helm: v3, k8s: v1, kustomize: v3, terraform: v12, v13, v14, tfplan: v1)
   -p, --policy-path stringArray   policy path directory
   -t, --policy-type strings       policy type (all, aws, azure, gcp, github, k8s) (default [all])
   -r, --remote-type string        type of remote backend (git, s3, gcs, http, terraform-registry)
diff --git a/test/e2e/scan/golden/scan_help.txt b/test/e2e/scan/golden/scan_help.txt
index 9c1f1c053..3e7a251f8 100644
--- a/test/e2e/scan/golden/scan_help.txt
+++ b/test/e2e/scan/golden/scan_help.txt
@@ -10,8 +10,8 @@ Flags:
   -h, --help                      help for scan
   -d, --iac-dir string            path to a directory containing one or more IaC files (default ".")
   -f, --iac-file string           path to a single IaC file
-  -i, --iac-type string           iac type (helm, k8s, kustomize, terraform)
-      --iac-version string        iac version (helm: v3, k8s: v1, kustomize: v3, terraform: v12, v13, v14)
+  -i, --iac-type string           iac type (helm, k8s, kustomize, terraform, tfplan)
+      --iac-version string        iac version (helm: v3, k8s: v1, kustomize: v3, terraform: v12, v13, v14, tfplan: v1)
   -p, --policy-path stringArray   policy path directory
   -t, --policy-type strings       policy type (all, aws, azure, gcp, github, k8s) (default [all])
   -r, --remote-type string        type of remote backend (git, s3, gcs, http, terraform-registry)