Repository: tenable/terrascan (language: python)

Issue:
tfplan should use resource address for id field
* terrascan version: 1.5.0
* Operating System: macOS

### Description

I'm attempting to identify which resources are in violation of policies. Being able to identify resources in the same or similar way as Terraform's own output and internal representation of these resources would be helpful. So instead of `null_resource.this` we would get `module.example.null_resource.this`. It would be helpful to have a better understanding of how the different resource representations within the plan file are merged by terrascan.

### What I Did

`./main.tf`
```hcl
resource "null_resource" "this" {
  provisioner "local-exec" {
    command = "echo 'Hello'"
  }
}
```

`./example/main.tf`
```hcl
module "example" {
  source "../"
}
```

`terrascan scan -i tfplan --config-only -o json`
```json
{
  "null_resource": [
    {
      "id": "null_resource.this",
      "name": "this",
      "source": "",
      "line": 0,
      "type": "null_resource",
      "config": {
        "triggers": null
      },
      "skip_rules": null
    }
  ]
}
```

In the plan file, these have the desired attribute under `planned_values` and `resource_changes`:
```json
{
  "format_version": "0.1",
  "terraform_version": "0.14.10",
  "planned_values": {
    "root_module": {
      "child_modules": [
        {
          "resources": [
            {
              "address": "module.example.null_resource.this",
...
  "resource_changes": [
    {
      "address": "module.example.null_resource.this",
...
```



Discussion / hints:

Fix patch:
diff --git a/pkg/iac-providers/kubernetes/v1/normalize.go b/pkg/iac-providers/kubernetes/v1/normalize.go
index 818a43b64..03a1687a6 100644
--- a/pkg/iac-providers/kubernetes/v1/normalize.go
+++ b/pkg/iac-providers/kubernetes/v1/normalize.go
@@ -134,7 +134,7 @@ func (k *K8sV1) Normalize(doc *utils.IacDocument) (*output.ResourceConfig, error
 			namespace = "default"
 		}
 
-		resourceConfig.ID = resourceConfig.Type + "." + resource.Metadata.NameOrGenerateName() + "." + namespace
+		resourceConfig.ID = resourceConfig.Type + "." + resource.Metadata.NameOrGenerateName() + "-" + namespace
 	}
 
 	// read and update skip rules, if present
diff --git a/pkg/iac-providers/output/types.go b/pkg/iac-providers/output/types.go
index 364ee4867..9c0bb725b 100644
--- a/pkg/iac-providers/output/types.go
+++ b/pkg/iac-providers/output/types.go
@@ -49,36 +49,6 @@ type SkipRule struct {
 // AllResourceConfigs is a list/slice of resource configs present in IaC
 type AllResourceConfigs map[string][]ResourceConfig
 
-// FindResourceByID Finds a given resource within the resource map and returns a reference to that resource
-func (a AllResourceConfigs) FindResourceByID(resourceID string) (*ResourceConfig, error) {
-	if len(a) == 0 {
-		return nil, fmt.Errorf("AllResourceConfigs is nil or doesn't contain any resource type")
-	}
-	resTypeName := strings.Split(resourceID, ".")
-	if len(resTypeName) < 2 {
-		return nil, fmt.Errorf("resource ID has an invalid format %s", resourceID)
-	}
-
-	resourceType := resTypeName[0]
-
-	found := false
-	var resource ResourceConfig
-	resourceTypeList := a[resourceType]
-	for i := range resourceTypeList {
-		if resourceTypeList[i].ID == resourceID {
-			resource = resourceTypeList[i]
-			found = true
-			break
-		}
-	}
-
-	if !found {
-		return nil, nil
-	}
-
-	return &resource, nil
-}
-
 // FindAllResourcesByID Finds all resources within the resource map
 func (a AllResourceConfigs) FindAllResourcesByID(resourceID string) ([]*ResourceConfig, error) {
 	if len(a) == 0 {
@@ -89,7 +59,7 @@ func (a AllResourceConfigs) FindAllResourcesByID(resourceID string) ([]*Resource
 		return nil, fmt.Errorf("resource ID has an invalid format %s", resourceID)
 	}
 
-	resourceType := resTypeName[0]
+	resourceType := resTypeName[len(resTypeName)-2]
 
 	resources := make([]*ResourceConfig, 0)
 	resourceTypeList := a[resourceType]
diff --git a/pkg/iac-providers/tfplan/v1/load-file.go b/pkg/iac-providers/tfplan/v1/load-file.go
index e19f92810..9ceceabcf 100644
--- a/pkg/iac-providers/tfplan/v1/load-file.go
+++ b/pkg/iac-providers/tfplan/v1/load-file.go
@@ -20,7 +20,6 @@ import (
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
-	"strings"
 
 	"github.com/accurics/terrascan/pkg/iac-providers/output"
 	"github.com/accurics/terrascan/pkg/utils"
@@ -74,7 +73,6 @@ func (t *TFPlan) LoadIacFile(absFilePath string) (allResourcesConfig output.AllR
 	// 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 {
@@ -106,12 +104,3 @@ func (t *TFPlan) isValidTFPlanJSON(tfjson []byte) error {
 
 	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:], ".")
-}


Test patch:
diff --git a/pkg/iac-providers/kubernetes/v1/normalize_test.go b/pkg/iac-providers/kubernetes/v1/normalize_test.go
index aeecea5ea..0aac143ed 100644
--- a/pkg/iac-providers/kubernetes/v1/normalize_test.go
+++ b/pkg/iac-providers/kubernetes/v1/normalize_test.go
@@ -215,7 +215,7 @@ func TestK8sV1Normalize(t *testing.T) {
 				},
 			},
 			want: &output.ResourceConfig{
-				ID:   "kubernetes_pod.myapp-pod.default",
+				ID:   "kubernetes_pod.myapp-pod-default",
 				Name: "myapp-pod",
 				Line: 0,
 				Type: "kubernetes_pod",
@@ -249,7 +249,7 @@ func TestK8sV1Normalize(t *testing.T) {
 				},
 			},
 			want: &output.ResourceConfig{
-				ID:   "kubernetes_crd.myapp-pod-prefix-.default",
+				ID:   "kubernetes_crd.myapp-pod-prefix--default",
 				Name: "myapp-pod-prefix-",
 				Line: 0,
 				Type: "kubernetes_crd",
diff --git a/pkg/iac-providers/output/types_test.go b/pkg/iac-providers/output/types_test.go
index d88a325f6..5c633eb78 100644
--- a/pkg/iac-providers/output/types_test.go
+++ b/pkg/iac-providers/output/types_test.go
@@ -21,78 +21,6 @@ import (
 	"testing"
 )
 
-func TestAllResourceConfigsFindResourceByID(t *testing.T) {
-	testResourceConfig := ResourceConfig{
-		ID: "s3.my_s3_bucket",
-	}
-
-	type args struct {
-		resourceID string
-	}
-	tests := []struct {
-		name    string
-		a       AllResourceConfigs
-		args    args
-		want    *ResourceConfig
-		wantErr bool
-	}{
-		{
-			name:    "nil AllResourceConfigs",
-			a:       nil,
-			args:    args{},
-			want:    nil,
-			wantErr: true,
-		},
-		{
-			name: "invalid resource id",
-			a: AllResourceConfigs{
-				"key": {},
-			},
-			args: args{
-				resourceID: "id",
-			},
-			want:    nil,
-			wantErr: true,
-		},
-		{
-			name: "resource present in AllResourceConfigs",
-			a: AllResourceConfigs{
-				"s3": {
-					testResourceConfig,
-				},
-			},
-			args: args{
-				resourceID: "s3.my_s3_bucket",
-			},
-			want: &testResourceConfig,
-		},
-		{
-			name: "resource not present in AllResourceConfigs",
-			a: AllResourceConfigs{
-				"s3": {
-					testResourceConfig,
-				},
-			},
-			args: args{
-				resourceID: "ec2.test_instance",
-			},
-			want: nil,
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			got, err := tt.a.FindResourceByID(tt.args.resourceID)
-			if (err != nil) != tt.wantErr {
-				t.Errorf("AllResourceConfigs.FindResourceByID() error = %v, wantErr %v", err, tt.wantErr)
-				return
-			}
-			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("AllResourceConfigs.FindResourceByID() = %v, want %v", got, tt.want)
-			}
-		})
-	}
-}
-
 func TestAllResourceConfigsGetResourceCount(t *testing.T) {
 	tests := []struct {
 		name      string
@@ -313,6 +241,10 @@ func TestAllResourceConfigsFindAllResourcesByID(t *testing.T) {
 		ID: "s3.my_s3_bucket",
 	}
 
+	testS3LongIDResourceConfig := ResourceConfig{
+		ID: "module.somemodule.s3.my_s3_bucket",
+	}
+
 	testResourceConfigList := []*ResourceConfig{&testS3ResourceConfig}
 
 	type args struct {
@@ -388,6 +320,20 @@ func TestAllResourceConfigsFindAllResourcesByID(t *testing.T) {
 			},
 			want: []*ResourceConfig{},
 		},
+		{
+			name: "long resource ID",
+			a: AllResourceConfigs{
+				"s3": {
+					testS3LongIDResourceConfig,
+				},
+			},
+			args: args{
+				resourceID: "module.somemodule.s3.my_s3_bucket",
+			},
+			want: []*ResourceConfig{
+				&testS3LongIDResourceConfig,
+			},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/pkg/iac-providers/tfplan/v1/load-file_test.go b/pkg/iac-providers/tfplan/v1/load-file_test.go
index 8a625f2ec..ffb720626 100644
--- a/pkg/iac-providers/tfplan/v1/load-file_test.go
+++ b/pkg/iac-providers/tfplan/v1/load-file_test.go
@@ -142,41 +142,3 @@ func TestIsValidTFPlanJSON(t *testing.T) {
 		})
 	}
 }
-
-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)
-		}
-	}
-
-}