Repository: tenable/terrascan (language: python)

Issue:
Add capability to extract references to container images in terraform



Discussion / hints:

Fix patch:
diff --git a/pkg/iac-providers/terraform/commons/extract-container-images.go b/pkg/iac-providers/terraform/commons/extract-container-images.go
new file mode 100644
index 000000000..2ecb63af7
--- /dev/null
+++ b/pkg/iac-providers/terraform/commons/extract-container-images.go
@@ -0,0 +1,273 @@
+/*
+    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 commons
+
+import (
+	"encoding/json"
+	"io/ioutil"
+	"path/filepath"
+	"strings"
+
+	"github.com/accurics/terrascan/pkg/iac-providers/output"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
+	hclConfigs "github.com/hashicorp/terraform/configs"
+	"go.uber.org/zap"
+)
+
+const (
+	hashiCorp              = "hashicorp"
+	container              = "container"
+	initContainer          = "init_container"
+	spec                   = "spec"
+	template               = "template"
+	kubernetes             = "kubernetes"
+	image                  = "image"
+	name                   = "name"
+	jobTemplate            = "job_template"
+	azureContainerResource = "azurerm_container_group"
+	awsContainerResources  = "aws_ecs_task_definition"
+	jsonCodeSuffix         = "${jsonencode("
+	fileSuffix             = `${file("`
+	containerDefinitions   = "container_definitions"
+)
+
+// all the type of resources which has container definitaions
+var k8sResources = map[string]struct{}{
+	"kubernetes_deployment":             {},
+	"kubernetes_pod":                    {},
+	"kubernetes_stateful_set":           {},
+	"kubernetes_job":                    {},
+	"kubernetes_cron_job":               {},
+	"kubernetes_daemonset":              {},
+	"kubernetes_replication_controller": {},
+}
+
+// isKuberneteResource - verifies resource is k8s type
+func isKuberneteResource(resource *hclConfigs.Resource) bool {
+	_, ok := k8sResources[resource.Type]
+	return ok
+}
+
+//isAzureConatinerResource verifies resource is azure type
+func isAzureConatinerResource(resource *hclConfigs.Resource) bool {
+	return resource.Type == azureContainerResource
+}
+
+//isAwsConatinerResource verifies resource is aws type
+func isAwsConatinerResource(resource *hclConfigs.Resource) bool {
+	return resource.Type == awsContainerResources
+}
+
+//fetchConatinersFromAzureResource extracts all the containers from azure resource
+func fetchContainersFromAzureResource(resource jsonObj) []output.ContainerNameAndImage {
+	results := []output.ContainerNameAndImage{}
+	if v, ok := resource[container]; ok {
+		if containers, vok := v.([]jsonObj); vok {
+			results = getContainers(containers)
+		}
+	}
+	return results
+}
+
+//fetchConatinersFromAwsResource extracts all the containers from aws ecs resource
+func fetchContainersFromAwsResource(resource jsonObj, hclBody *hclsyntax.Body, resourcePath string) []output.ContainerNameAndImage {
+	results := []output.ContainerNameAndImage{}
+	if v, ok := resource[containerDefinitions]; ok {
+		def := v.(string)
+		if strings.HasPrefix(def, jsonCodeSuffix) {
+			return getContainersFromhclBody(hclBody)
+		} else if strings.HasPrefix(def, fileSuffix) {
+			fileLocation := strings.TrimSpace(def)
+			fileLocation = strings.TrimPrefix(fileLocation, fileSuffix)
+			fileLocation = strings.TrimSuffix(fileLocation, `")}`)
+			dir := filepath.Dir(resourcePath)
+			if !filepath.IsAbs(fileLocation) {
+				fileLocation = filepath.Join(dir, fileLocation)
+			}
+			fileData, err := ioutil.ReadFile(fileLocation)
+			if err != nil {
+				zap.S().Errorf("error fetching containers from aws resource: %v", err)
+				return results
+			}
+			def = string(fileData)
+		}
+		containers := []jsonObj{}
+		err := json.Unmarshal([]byte(def), &containers)
+		if err != nil {
+			zap.S().Errorf("error fetching containers from aws resource: %v", err)
+			return results
+		}
+		results = getContainers(containers)
+	}
+	return results
+}
+
+//getContainersFromhclBody parses the attribute and creates container object
+func getContainersFromhclBody(hclBody *hclsyntax.Body) (results []output.ContainerNameAndImage) {
+	for _, v := range hclBody.Attributes {
+		if v.Name == containerDefinitions {
+			switch v.Expr.(type) {
+			case *hclsyntax.FunctionCallExpr:
+				funcExp := v.Expr.(*hclsyntax.FunctionCallExpr)
+				for _, arg := range funcExp.Args {
+					re, diags := arg.Value(nil)
+					if diags.HasErrors() {
+						zap.S().Errorf("error fetching containers from aws resource: %v", getErrorMessagesFromDiagnostics(diags))
+						return
+					}
+					if !re.CanIterateElements() {
+						return
+					}
+					it := re.ElementIterator()
+					for it.Next() {
+						_, val := it.Element()
+						containerTemp, err := ctyToMap(val)
+						if err != nil {
+							zap.S().Errorf("error fetching containers from aws resource: %v", err)
+							return
+						}
+						containerMap := containerTemp.(map[string]interface{})
+						tempContainer := output.ContainerNameAndImage{}
+						if image, iok := containerMap[image]; iok {
+							if imageName, ok := image.(string); ok {
+								tempContainer.Image = imageName
+							}
+						}
+						if name, nok := containerMap[name]; nok {
+							if containerName, ok := name.(string); ok {
+								tempContainer.Name = containerName
+							}
+						}
+						if tempContainer.Name == "" && tempContainer.Image == "" {
+							continue
+						}
+						results = append(results, tempContainer)
+					}
+				}
+			}
+			break
+		}
+	}
+	return
+}
+
+// getContainers reads and creates container config
+func getContainers(containers []jsonObj) (results []output.ContainerNameAndImage) {
+	for _, container := range containers {
+		tempContainer := output.ContainerNameAndImage{}
+		if image, iok := container[image]; iok {
+			if imageName, ok := image.(string); ok {
+				tempContainer.Image = imageName
+			}
+		}
+		if name, nok := container[name]; nok {
+			if containerName, ok := name.(string); ok {
+				tempContainer.Name = containerName
+			}
+		}
+		if tempContainer.Name == "" && tempContainer.Image == "" {
+			continue
+		}
+		results = append(results, tempContainer)
+	}
+	return
+}
+
+//extractContainerImagesFromk8sResources extracts containers from k8s resource
+func extractContainerImagesFromk8sResources(resource *hclConfigs.Resource, body *hclsyntax.Body) (containers, initContainers []output.ContainerNameAndImage) {
+	for _, block := range body.Blocks {
+		if block.Type == spec {
+			containerBlocks, initContainerBlocks := getContainerAndInitContainerFromSpecBlocks(block.Body)
+			containers = getContainerConfigFromContainerBlock(containerBlocks)
+			initContainers = getContainerConfigFromContainerBlock(initContainerBlocks)
+
+		}
+	}
+	return
+}
+
+// getContainerAndInitContainerFromSpecBlocks extracts container config from spec block of resource
+func getContainerAndInitContainerFromSpecBlocks(specs *hclsyntax.Body) (containers, initContainers []*hclsyntax.Block) {
+	for _, block := range specs.Blocks {
+		if block.Type == template {
+			return getContainerAndInitContainerFromTemplateBlocks(block.Body.Blocks)
+		} else if block.Type == jobTemplate {
+			for _, jobTemplateBlock := range block.Body.Blocks {
+				if jobTemplateBlock.Type == spec {
+					return getContainerAndInitContainerFromSpecBlocks(jobTemplateBlock.Body)
+				}
+			}
+		} else if block.Type == container {
+			containers = append(containers, block)
+		}
+	}
+	return
+}
+
+//getContainerAndInitContainerFromTemplateBlocks extracts container config from template block of resource
+func getContainerAndInitContainerFromTemplateBlocks(templateBlocks []*hclsyntax.Block) (containers, initContainers []*hclsyntax.Block) {
+	for _, templateBlocks := range templateBlocks {
+		if templateBlocks.Type == spec {
+			for _, specBlocks := range templateBlocks.Body.Blocks {
+				if specBlocks.Type == container {
+					containers = append(containers, specBlocks)
+				} else if specBlocks.Type == initContainer {
+					initContainers = append(initContainers, specBlocks)
+				}
+			}
+		}
+	}
+	return
+}
+
+//getContainerConfigFromContainerBlock creates container config from container block of resource
+func getContainerConfigFromContainerBlock(containerBlocks []*hclsyntax.Block) (containerImages []output.ContainerNameAndImage) {
+	for _, conatainerBlock := range containerBlocks {
+		containerImage := output.ContainerNameAndImage{}
+		for _, attr := range conatainerBlock.Body.Attributes {
+			if attr.Name == image {
+				containerImage.Image = getValueFromCtyExpr(attr.Expr)
+			}
+			if attr.Name == name {
+				containerImage.Name = getValueFromCtyExpr(attr.Expr)
+			}
+		}
+		if containerImage.Image == "" && containerImage.Name == "" {
+			continue
+		}
+		containerImages = append(containerImages, containerImage)
+	}
+	return
+}
+
+//getValueFromCtyExpr get value string from hcl expression
+func getValueFromCtyExpr(expr hclsyntax.Expression) (value string) {
+	val, diags := expr.Value(nil)
+	if diags.HasErrors() {
+		zap.S().Errorf("error fetching containers from k8s resource: %v", getErrorMessagesFromDiagnostics(diags))
+		return
+	}
+	valInterface, err := ctyToStr(val)
+	if err != nil {
+		zap.S().Errorf("error fetching containers from k8s resource: %v", err)
+		return
+	}
+	if containerName, ok := valInterface.(string); ok {
+		value = containerName
+	}
+	return
+}
diff --git a/pkg/iac-providers/terraform/commons/load-dir.go b/pkg/iac-providers/terraform/commons/load-dir.go
index 624a25810..77d208c57 100644
--- a/pkg/iac-providers/terraform/commons/load-dir.go
+++ b/pkg/iac-providers/terraform/commons/load-dir.go
@@ -150,7 +150,6 @@ func (t TerraformDirectoryLoader) loadDirRecursive(dirList []string) (output.All
 
 		// get unified config for the current directory
 		unified, diags := t.buildUnifiedConfig(rootMod, dir)
-
 		// Get the downloader chache
 		remoteURLMapping := t.remoteDownloader.GetDownloaderCache()
 
@@ -187,7 +186,6 @@ func (t TerraformDirectoryLoader) loadDirRecursive(dirList []string) (output.All
 
 			// reference resolver
 			r := NewRefResolver(current.Config, current.ParentModuleCall)
-
 			// traverse through all current's resources
 			for _, managedResource := range current.Config.Module.ManagedResources {
 
diff --git a/pkg/iac-providers/terraform/commons/load-file.go b/pkg/iac-providers/terraform/commons/load-file.go
index 990414ae3..3ed00da25 100644
--- a/pkg/iac-providers/terraform/commons/load-file.go
+++ b/pkg/iac-providers/terraform/commons/load-file.go
@@ -34,12 +34,14 @@ func LoadIacFile(absFilePath string) (allResourcesConfig output.AllResourceConfi
 	// new terraform config parser
 	parser := hclConfigs.NewParser(afero.NewOsFs())
 
+	// load current iac file
 	hclFile, diags := parser.LoadConfigFile(absFilePath)
 	if hclFile == nil {
 		errMessage := fmt.Sprintf("error occured while loading config file '%s'. error:\n%v\n", absFilePath, getErrorMessagesFromDiagnostics(diags))
 		zap.S().Debug(errMessage)
 		return allResourcesConfig, fmt.Errorf(errMessage)
 	}
+
 	if diags != nil {
 		errMessage := fmt.Sprintf("failed to load iac file '%s'. error:\n%v\n", absFilePath, getErrorMessagesFromDiagnostics(diags))
 		zap.S().Debug(errMessage)
diff --git a/pkg/iac-providers/terraform/commons/resource.go b/pkg/iac-providers/terraform/commons/resource.go
index c2f82ec3a..7aaea3307 100644
--- a/pkg/iac-providers/terraform/commons/resource.go
+++ b/pkg/iac-providers/terraform/commons/resource.go
@@ -44,6 +44,7 @@ func CreateResourceConfig(managedResource *hclConfigs.Resource) (resourceConfig
 	if hclBody, ok = managedResource.Config.(*hclsyntax.Body); !ok {
 		return resourceConfig, fmt.Errorf("failed type assertion for hcl.Body in *hclConfigs.Resource. error: expected hcl.Body type is *hclsyntax.Body, but got %T", managedResource.Config)
 	}
+
 	goOut, err := c.convertBody(hclBody)
 	if err != nil {
 		zap.S().Errorf("failed to convert hcl.Body to go struct; resource '%s', file: '%s'. error: '%v'",
@@ -52,20 +53,36 @@ func CreateResourceConfig(managedResource *hclConfigs.Resource) (resourceConfig
 	}
 
 	minSeverity, maxSeverity := utils.GetMinMaxSeverity(c.rangeSource(hclBody.Range()))
+
+	containers, initContainers := findContainers(managedResource, goOut, hclBody)
 	// create a resource config
 	resourceConfig = output.ResourceConfig{
-		ID:          fmt.Sprintf("%s.%s", managedResource.Type, managedResource.Name),
-		Name:        managedResource.Name,
-		Type:        managedResource.Type,
-		Source:      managedResource.DeclRange.Filename,
-		Line:        managedResource.DeclRange.Start.Line,
-		Config:      goOut,
-		SkipRules:   utils.GetSkipRules(c.rangeSource(hclBody.Range())),
-		MaxSeverity: maxSeverity,
-		MinSeverity: minSeverity,
+		ID:                  fmt.Sprintf("%s.%s", managedResource.Type, managedResource.Name),
+		Name:                managedResource.Name,
+		Type:                managedResource.Type,
+		Source:              managedResource.DeclRange.Filename,
+		Line:                managedResource.DeclRange.Start.Line,
+		Config:              goOut,
+		SkipRules:           utils.GetSkipRules(c.rangeSource(hclBody.Range())),
+		MaxSeverity:         maxSeverity,
+		MinSeverity:         minSeverity,
+		ContainerImages:     containers,
+		InitContainerImages: initContainers,
 	}
 
 	// successful
 	zap.S().Debugf("created resource config for resource '%s', file: '%s'", resourceConfig.Name, resourceConfig.Source)
 	return resourceConfig, nil
 }
+
+//findContainers finds containers defined in resource
+func findContainers(managedResource *hclConfigs.Resource, jsonBody jsonObj, hclBody *hclsyntax.Body) (containers []output.ContainerNameAndImage, initContainers []output.ContainerNameAndImage) {
+	if isKuberneteResource(managedResource) {
+		containers, initContainers = extractContainerImagesFromk8sResources(managedResource, hclBody)
+	} else if isAzureConatinerResource(managedResource) {
+		containers = fetchContainersFromAzureResource(jsonBody)
+	} else if isAwsConatinerResource(managedResource) {
+		containers = fetchContainersFromAwsResource(jsonBody, hclBody, managedResource.DeclRange.Filename)
+	}
+	return
+}


Test patch:
diff --git a/pkg/iac-providers/terraform/commons/load-dir_test.go b/pkg/iac-providers/terraform/commons/load-dir_test.go
index 8bd29f2a4..8cbaa2fb2 100644
--- a/pkg/iac-providers/terraform/commons/load-dir_test.go
+++ b/pkg/iac-providers/terraform/commons/load-dir_test.go
@@ -17,15 +17,21 @@
 package commons
 
 import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
 	"os"
 	"path/filepath"
 	"testing"
 
 	"github.com/accurics/terrascan/pkg/downloader"
 	"github.com/accurics/terrascan/pkg/iac-providers/output"
+	"github.com/accurics/terrascan/pkg/iac-providers/terraform/commons/test"
 	"github.com/accurics/terrascan/pkg/utils"
+	"github.com/hashicorp/go-multierror"
 	"github.com/hashicorp/hcl/v2"
 	hclConfigs "github.com/hashicorp/terraform/configs"
+	"github.com/spf13/afero"
 	"go.uber.org/zap"
 )
 
@@ -40,6 +46,10 @@ var (
 		SourceAddr: testLocalSourceAddr,
 		CallRange:  hcl.Range{Filename: testFileNamePath},
 	}
+
+	invalidDirErrStringTemplate = "directory '%s' has no terraform config files"
+	testDataDir                 = "testdata"
+	tfJSONDir                   = filepath.Join(testDataDir, "tfjson")
 )
 
 func TestProcessLocalSource(t *testing.T) {
@@ -324,3 +334,69 @@ func TestGetRemoteModuleIfPresentInTerraformSrc(t *testing.T) {
 		})
 	}
 }
+
+func TestTerraformDirectoryLoaderLoadIacDir(t *testing.T) {
+	var nilMultiErr *multierror.Error = nil
+	tests := []struct {
+		name        string
+		tfConfigDir string
+		tfJSONFile  string
+		options     map[string]interface{}
+		wantErr     error
+	}{
+		{
+			name:        "directory with resources having container defined",
+			tfConfigDir: filepath.Join(testDataDir, "terraform-container-extraction"),
+			tfJSONFile:  filepath.Join(tfJSONDir, "output-with-containers.json"),
+			wantErr: multierror.Append(
+				fmt.Errorf(invalidDirErrStringTemplate, filepath.Join(testDataDir, "terraform-container-extraction")),
+				fmt.Errorf(invalidDirErrStringTemplate, filepath.Join(testDataDir, "terraform-container-extraction/terraform-aws-provider/task-definitions")),
+			),
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			tr := TerraformDirectoryLoader{
+				absRootDir:               tt.tfConfigDir,
+				remoteDownloader:         downloader.NewRemoteDownloader(),
+				parser:                   hclConfigs.NewParser(afero.NewOsFs()),
+				terraformInitModuleCache: make(map[string]TerraformModuleManifest),
+			}
+			got, gotErr := tr.LoadIacDir()
+			me, ok := gotErr.(*multierror.Error)
+			if !ok {
+				t.Errorf("expected multierror.Error, got %T", gotErr)
+			}
+			if tt.wantErr == nilMultiErr {
+				if err := me.ErrorOrNil(); err != nil {
+					t.Errorf("unexpected error; gotErr: '%v', wantErr: '%v'", gotErr, tt.wantErr)
+				}
+			} else if me.Error() != tt.wantErr.Error() {
+				t.Errorf("unexpected error; gotErr: '%v', wantErr: '%v'", gotErr, tt.wantErr)
+			}
+
+			var want output.AllResourceConfigs
+
+			// Read the expected value and unmarshal into want
+			contents, _ := ioutil.ReadFile(tt.tfJSONFile)
+			if utils.IsWindowsPlatform() {
+				contents = utils.ReplaceWinNewLineBytes(contents)
+			}
+
+			err := json.Unmarshal(contents, &want)
+			if err != nil {
+				t.Errorf("unexpected error unmarshalling want: %v", err)
+			}
+
+			match, err := test.IdenticalAllResourceConfigs(got, want)
+			if err != nil {
+				t.Errorf("unexpected error checking result: %v", err)
+			}
+			if !match {
+				g, _ := json.MarshalIndent(got, "", "  ")
+				w, _ := json.MarshalIndent(want, "", "  ")
+				t.Errorf("got '%v', want: '%v'", string(g), string(w))
+			}
+		})
+	}
+}
diff --git a/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-aws-provider/file_in_task_definition.tf b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-aws-provider/file_in_task_definition.tf
new file mode 100644
index 000000000..ad2045622
--- /dev/null
+++ b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-aws-provider/file_in_task_definition.tf
@@ -0,0 +1,16 @@
+resource "aws_ecs_task_definition" "service" {
+  family                = "service"
+  container_definitions = file("task-definitions/service.json")
+
+  proxy_configuration {
+    type           = "APPMESH"
+    container_name = "applicationContainerName"
+    properties = {
+      AppPorts         = "8080"
+      EgressIgnoredIPs = "169.254.170.2,169.254.169.254"
+      IgnoredUID       = "1337"
+      ProxyEgressPort  = 15001
+      ProxyIngressPort = 15000
+    }
+  }
+}
\ No newline at end of file
diff --git a/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-aws-provider/jsoncode_in_task_definition.tf b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-aws-provider/jsoncode_in_task_definition.tf
new file mode 100644
index 000000000..7606c3afc
--- /dev/null
+++ b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-aws-provider/jsoncode_in_task_definition.tf
@@ -0,0 +1,37 @@
+resource "aws_ecs_task_definition" "demo-ecs-jsoncode-task-definition" {
+  family                   = "ecs-task-definition-demo"
+  network_mode             = "awsvpc"
+  requires_compatibilities = ["FARGATE"]
+  memory                   = "1024"
+  cpu                      = "512"
+  execution_role_arn       = "arn:aws:iam::123456789012:role/ecsTaskExecutionRole"
+  container_definitions    =  jsonencode([
+    {
+      name      = "first"
+      image     = "service-first"
+      cpu       = 10
+      memory    = 512
+      essential = true
+      portMappings = [
+        {
+          containerPort = 80
+          hostPort      = 80
+        }
+      ]
+    },
+    {
+      name      = "second"
+      image     = "service-second"
+      cpu       = 10
+      memory    = 256
+      essential = true
+      portMappings = [
+        {
+          containerPort = 443
+          hostPort      = 443
+        }
+      ]
+    }
+  ])
+
+}
\ No newline at end of file
diff --git a/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-aws-provider/main.tf b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-aws-provider/main.tf
new file mode 100644
index 000000000..693a5d436
--- /dev/null
+++ b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-aws-provider/main.tf
@@ -0,0 +1,37 @@
+resource "aws_ecs_task_definition" "demo-ecs-task-definition" {
+  family                   = "ecs-task-definition-demo"
+  network_mode             = "awsvpc"
+  requires_compatibilities = ["FARGATE"]
+  memory                   = "1024"
+  cpu                      = "512"
+  execution_role_arn       = "arn:aws:iam::123456789012:role/ecsTaskExecutionRole"
+  container_definitions    =  <<TASK_DEFINITION
+[
+    {
+        "cpu": 10,
+        "command": ["sleep", "10"],
+        "entryPoint": ["/"],
+        "environment": [
+            {"name": "VARNAME", "value": "VARVAL"}
+        ],
+        "essential": true,
+        "image": "jenkins",
+        "memory": 128,
+        "name": "jenkins",
+        "portMappings": [
+            {
+                "containerPort": 80,
+                "hostPort": 8080
+            }
+        ],
+        "resourceRequirements":[
+            {
+                "type":"InferenceAccelerator",
+                "value":"device_1"
+            }
+        ]
+    }
+]
+TASK_DEFINITION
+
+}
\ No newline at end of file
diff --git a/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-aws-provider/task-definitions/service.json b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-aws-provider/task-definitions/service.json
new file mode 100644
index 000000000..eb1bec7b5
--- /dev/null
+++ b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-aws-provider/task-definitions/service.json
@@ -0,0 +1,26 @@
+[
+    {
+        "cpu": 10,
+        "command": ["sleep", "10"],
+        "entryPoint": ["/"],
+        "environment": [
+            {"name": "VARNAME", "value": "VARVAL"}
+        ],
+        "essential": true,
+        "image": "jenkins",
+        "memory": 128,
+        "name": "jenkins",
+        "portMappings": [
+            {
+                "containerPort": 80,
+                "hostPort": 8080
+            }
+        ],
+        "resourceRequirements":[
+            {
+                "type":"InferenceAccelerator",
+                "value":"device_1"
+            }
+        ]
+    }
+]
\ No newline at end of file
diff --git a/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-azure-provider/main.tf b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-azure-provider/main.tf
new file mode 100644
index 000000000..156cbdae5
--- /dev/null
+++ b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-azure-provider/main.tf
@@ -0,0 +1,31 @@
+resource "azurerm_container_group" "example" {
+  name                = "example-continst"
+  location            = azurerm_resource_group.example.location
+  resource_group_name = azurerm_resource_group.example.name
+  ip_address_type     = "public"
+  dns_name_label      = "aci-label"
+  os_type             = "Linux"
+
+  container {
+    name   = "hello-world"
+    image  = "microsoft/aci-helloworld:latest"
+    cpu    = "0.5"
+    memory = "1.5"
+
+    ports {
+      port     = 443
+      protocol = "TCP"
+    }
+  }
+
+  container {
+    name   = "sidecar"
+    image  = "microsoft/aci-tutorial-sidecar"
+    cpu    = "0.5"
+    memory = "1.5"
+  }
+
+  tags = {
+    environment = "testing"
+  }
+}
diff --git a/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-k8s-provider/cron_job.tf b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-k8s-provider/cron_job.tf
new file mode 100644
index 000000000..6538bb6f7
--- /dev/null
+++ b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-k8s-provider/cron_job.tf
@@ -0,0 +1,29 @@
+resource "kubernetes_cron_job" "demo" {
+  metadata {
+    name = "demo"
+  }
+  spec {
+    concurrency_policy            = "Replace"
+    failed_jobs_history_limit     = 5
+    schedule                      = "1 0 * * *"
+    starting_deadline_seconds     = 10
+    successful_jobs_history_limit = 10
+    job_template {
+      metadata {}
+      spec {
+        backoff_limit              = 2
+        ttl_seconds_after_finished = 10
+        template {
+          metadata {}
+          spec {
+            container {
+              name    = "hello"
+              image   = "busybox"
+              command = ["/bin/sh", "-c", "date; echo Hello from the Kubernetes cluster"]
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-k8s-provider/deployment.tf b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-k8s-provider/deployment.tf
new file mode 100644
index 000000000..734c856c2
--- /dev/null
+++ b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-k8s-provider/deployment.tf
@@ -0,0 +1,83 @@
+terraform {
+ required_providers {
+   kubernetes-beta = {
+     source  = "hashicorp/kubernetes"
+     version = "= 2.3.2"
+   }
+ }
+}
+
+provider "kubernetes-beta" {
+  config_path = "~/.kube/config"
+}
+resource "kubernetes_namespace" "test" {
+  metadata {
+    name = "nginx"
+  }
+}
+
+resource "kubernetes_service" "test" {
+  metadata {
+    name      = "nginx"
+    namespace = kubernetes_namespace.test.metadata.0.name
+  }
+  spec {
+    selector = {
+      app = kubernetes_deployment.example.spec.0.template.0.metadata.0.labels.test
+    }
+    type = "NodePort"
+    port {
+      node_port   = 30201
+      port        = 80
+      target_port = 80
+    }
+  }
+}
+
+resource "kubernetes_deployment" "example" {
+  metadata {
+    name = "terraform-example"
+    namespace = kubernetes_namespace.test.metadata.0.name
+    labels = {
+      test = "MyExampleApp"
+    }
+  }
+
+  spec {
+    replicas = 1
+
+    selector {
+      match_labels = {
+        test = "MyExampleApp"
+      }
+    }
+
+    template {
+      metadata {
+        labels = {
+          test = "MyExampleApp"
+        }
+      }
+
+      spec {
+        container {
+          image = "nginx:1.7.8"
+          name  = "example1"
+        }
+
+        container {
+          image = "nginx:1.7.8"
+          name  = "example2"
+        }
+
+        container {
+          image = "nginx:1.7.8"
+          name  = "example3"
+        }
+
+        service_account_name            = "default"
+        automount_service_account_token = false
+      }
+    }
+  }
+}
diff --git a/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-k8s-provider/job.tf b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-k8s-provider/job.tf
new file mode 100644
index 000000000..5039bd420
--- /dev/null
+++ b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-k8s-provider/job.tf
@@ -0,0 +1,20 @@
+resource "kubernetes_job" "demo" {
+  metadata {
+    name = "demo"
+  }
+  spec {
+    template {
+      metadata {}
+      spec {
+        container {
+          name    = "pi"
+          image   = "perl"
+          command = ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
+        }
+        restart_policy = "Never"
+      }
+    }
+    backoff_limit = 4
+  }
+  wait_for_completion = true
+}
diff --git a/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-k8s-provider/pod.tf b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-k8s-provider/pod.tf
new file mode 100644
index 000000000..54b4d88df
--- /dev/null
+++ b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-k8s-provider/pod.tf
@@ -0,0 +1,26 @@
+resource "kubernetes_pod" "test" {
+  metadata {
+    name = "terraform-example"
+  }
+
+  spec {
+    container {
+      image = "nginx:1.7.9"
+      name  = "example"
+
+      resources {
+        limits = {
+          cpu          = "0.5"
+          memory       = "512Mi"
+          "nvidia/gpu" = "1"
+        }
+
+        requests = {
+          cpu          = "250m"
+          memory       = "50Mi"
+          "nvidia/gpu" = "1"
+        }
+      }
+    }
+  }
+}
diff --git a/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-k8s-provider/rc.tf b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-k8s-provider/rc.tf
new file mode 100644
index 000000000..4c0caeaf2
--- /dev/null
+++ b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-k8s-provider/rc.tf
@@ -0,0 +1,57 @@
+resource "kubernetes_replication_controller" "example" {
+  metadata {
+    name = "terraform-example"
+    labels = {
+      test = "MyExampleApp"
+    }
+  }
+
+  spec {
+    selector = {
+      test = "MyExampleApp"
+    }
+    template {
+      metadata {
+        labels = {
+          test = "MyExampleApp"
+        }
+        annotations = {
+          "key1" = "value1"
+        }
+      }
+
+      spec {
+        container {
+          image = "nginx:1.7.8"
+          name  = "example"
+
+          liveness_probe {
+            http_get {
+              path = "/nginx_status"
+              port = 8080
+
+              http_header {
+                name  = "X-Custom-Header"
+                value = "Awesome"
+              }
+            }
+
+            initial_delay_seconds = 3
+            period_seconds        = 3
+          }
+
+          resources {
+            limits = {
+              cpu    = "0.5"
+              memory = "512Mi"
+            }
+            requests = {
+              cpu    = "250m"
+              memory = "50Mi"
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-k8s-provider/stateful_set.tf b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-k8s-provider/stateful_set.tf
new file mode 100644
index 000000000..64847378c
--- /dev/null
+++ b/pkg/iac-providers/terraform/commons/testdata/terraform-container-extraction/terraform-k8s-provider/stateful_set.tf
@@ -0,0 +1,183 @@
+resource "kubernetes_stateful_set" "prometheus" {
+  metadata {
+    annotations = {
+      SomeAnnotation = "foobar"
+    }
+
+    labels = {
+      k8s-app                           = "prometheus"
+      "kubernetes.io/cluster-service"   = "true"
+      "addonmanager.kubernetes.io/mode" = "Reconcile"
+      version                           = "v2.2.1"
+    }
+
+    name = "prometheus"
+  }
+
+  spec {
+    pod_management_policy  = "Parallel"
+    replicas               = 1
+    revision_history_limit = 5
+
+    selector {
+      match_labels = {
+        k8s-app = "prometheus"
+      }
+    }
+
+    service_name = "prometheus"
+
+    template {
+      metadata {
+        labels = {
+          k8s-app = "prometheus"
+        }
+
+        annotations = {}
+      }
+
+      spec {
+        service_account_name = "prometheus"
+
+        init_container {
+          name              = "init-chown-data"
+          image             = "busybox:latest"
+          image_pull_policy = "IfNotPresent"
+          command           = ["chown", "-R", "65534:65534", "/data"]
+
+          volume_mount {
+            name       = "prometheus-data"
+            mount_path = "/data"
+            sub_path   = ""
+          }
+        }
+
+        container {
+          name              = "prometheus-server-configmap-reload"
+          image             = "jimmidyson/configmap-reload:v0.1"
+          image_pull_policy = "IfNotPresent"
+
+          args = [
+            "--volume-dir=/etc/config",
+            "--webhook-url=http://localhost:9090/-/reload",
+          ]
+
+          volume_mount {
+            name       = "config-volume"
+            mount_path = "/etc/config"
+            read_only  = true
+          }
+
+          resources {
+            limits = {
+              cpu    = "10m"
+              memory = "10Mi"
+            }
+
+            requests = {
+              cpu    = "10m"
+              memory = "10Mi"
+            }
+          }
+        }
+
+        container {
+          name              = "prometheus-server"
+          image             = "prom/prometheus:v2.2.1"
+          image_pull_policy = "IfNotPresent"
+
+          args = [
+            "--config.file=/etc/config/prometheus.yml",
+            "--storage.tsdb.path=/data",
+            "--web.console.libraries=/etc/prometheus/console_libraries",
+            "--web.console.templates=/etc/prometheus/consoles",
+            "--web.enable-lifecycle",
+          ]
+
+          port {
+            container_port = 9090
+          }
+
+          resources {
+            limits = {
+              cpu    = "200m"
+              memory = "1000Mi"
+            }
+
+            requests = {
+              cpu    = "200m"
+              memory = "1000Mi"
+            }
+          }
+
+          volume_mount {
+            name       = "config-volume"
+            mount_path = "/etc/config"
+          }
+
+          volume_mount {
+            name       = "prometheus-data"
+            mount_path = "/data"
+            sub_path   = ""
+          }
+
+          readiness_probe {
+            http_get {
+              path = "/-/ready"
+              port = 9090
+            }
+
+            initial_delay_seconds = 30
+            timeout_seconds       = 30
+          }
+
+          liveness_probe {
+            http_get {
+              path   = "/-/healthy"
+              port   = 9090
+              scheme = "HTTPS"
+            }
+
+            initial_delay_seconds = 30
+            timeout_seconds       = 30
+          }
+        }
+
+        termination_grace_period_seconds = 300
+
+        volume {
+          name = "config-volume"
+
+          config_map {
+            name = "prometheus-config"
+          }
+        }
+      }
+    }
+
+    update_strategy {
+      type = "RollingUpdate"
+
+      rolling_update {
+        partition = 1
+      }
+    }
+
+    volume_claim_template {
+      metadata {
+        name = "prometheus-data"
+      }
+
+      spec {
+        access_modes       = ["ReadWriteOnce"]
+        storage_class_name = "standard"
+
+        resources {
+          requests = {
+            storage = "16Gi"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/pkg/iac-providers/terraform/commons/testdata/tfjson/output-with-containers.json b/pkg/iac-providers/terraform/commons/testdata/tfjson/output-with-containers.json
new file mode 100644
index 000000000..7a01e88ea
--- /dev/null
+++ b/pkg/iac-providers/terraform/commons/testdata/tfjson/output-with-containers.json
@@ -0,0 +1,804 @@
+{
+  "aws_ecs_task_definition": [
+    {
+      "id": "aws_ecs_task_definition.service",
+      "name": "service",
+      "module_name": "root",
+      "source": "terraform-aws-provider/file_in_task_definition.tf",
+      "plan_root": "terraform-aws-provider",
+      "line": 1,
+      "type": "aws_ecs_task_definition",
+      "config": {
+        "container_definitions": "${file(\"task-definitions/service.json\")}",
+        "family": "service",
+        "proxy_configuration": [
+          {
+            "container_name": "applicationContainerName",
+            "properties": {
+              "AppPorts": "8080",
+              "EgressIgnoredIPs": "169.254.170.2,169.254.169.254",
+              "IgnoredUID": "1337",
+              "ProxyEgressPort": 15001,
+              "ProxyIngressPort": 15000
+            },
+            "type": "APPMESH"
+          }
+        ]
+      },
+      "skip_rules": null,
+      "max_severity": "",
+      "min_severity": "",
+      "container_images": [
+        {
+          "name": "jenkins",
+          "image": "jenkins"
+        }
+      ]
+    },
+    {
+      "id": "aws_ecs_task_definition.demo-ecs-jsoncode-task-definition",
+      "name": "demo-ecs-jsoncode-task-definition",
+      "module_name": "root",
+      "source": "terraform-aws-provider/jsoncode_in_task_definition.tf",
+      "plan_root": "terraform-aws-provider",
+      "line": 1,
+      "type": "aws_ecs_task_definition",
+      "config": {
+        "container_definitions": "${jsonencode([\n    {\n      name      = \"first\"\n      image     = \"service-first\"\n      cpu       = 10\n      memory    = 512\n      essential = true\n      portMappings = [\n        {\n          containerPort = 80\n          hostPort      = 80\n        }\n      ]\n    },\n    {\n      name      = \"second\"\n      image     = \"service-second\"\n      cpu       = 10\n      memory    = 256\n      essential = true\n      portMappings = [\n        {\n          containerPort = 443\n          hostPort      = 443\n        }\n      ]\n    }\n  ])}",
+        "cpu": "512",
+        "execution_role_arn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
+        "family": "ecs-task-definition-demo",
+        "memory": "1024",
+        "network_mode": "awsvpc",
+        "requires_compatibilities": [
+          "FARGATE"
+        ]
+      },
+      "skip_rules": null,
+      "max_severity": "",
+      "min_severity": "",
+      "container_images": [
+        {
+          "name": "first",
+          "image": "service-first"
+        },
+        {
+          "name": "second",
+          "image": "service-second"
+        }
+      ]
+    },
+    {
+      "id": "aws_ecs_task_definition.demo-ecs-task-definition",
+      "name": "demo-ecs-task-definition",
+      "module_name": "root",
+      "source": "terraform-aws-provider/main.tf",
+      "plan_root": "terraform-aws-provider",
+      "line": 1,
+      "type": "aws_ecs_task_definition",
+      "config": {
+        "container_definitions": "[\n    {\n        \"cpu\": 10,\n        \"command\": [\"sleep\", \"10\"],\n        \"entryPoint\": [\"/\"],\n        \"environment\": [\n            {\"name\": \"VARNAME\", \"value\": \"VARVAL\"}\n        ],\n        \"essential\": true,\n        \"image\": \"jenkins\",\n        \"memory\": 128,\n        \"name\": \"jenkins\",\n        \"portMappings\": [\n            {\n                \"containerPort\": 80,\n                \"hostPort\": 8080\n            }\n        ],\n        \"resourceRequirements\":[\n            {\n                \"type\":\"InferenceAccelerator\",\n                \"value\":\"device_1\"\n            }\n        ]\n    }\n]\n",
+        "cpu": "512",
+        "execution_role_arn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
+        "family": "ecs-task-definition-demo",
+        "memory": "1024",
+        "network_mode": "awsvpc",
+        "requires_compatibilities": [
+          "FARGATE"
+        ]
+      },
+      "skip_rules": null,
+      "max_severity": "",
+      "min_severity": "",
+      "container_images": [
+        {
+          "name": "jenkins",
+          "image": "jenkins"
+        }
+      ]
+    }
+  ],
+  "azurerm_container_group": [
+    {
+      "id": "azurerm_container_group.example",
+      "name": "example",
+      "module_name": "root",
+      "source": "terraform-azure-provider/main.tf",
+      "plan_root": "terraform-azure-provider",
+      "line": 1,
+      "type": "azurerm_container_group",
+      "config": {
+        "container": [
+          {
+            "cpu": "0.5",
+            "image": "microsoft/aci-helloworld:latest",
+            "memory": "1.5",
+            "name": "hello-world",
+            "ports": [
+              {
+                "port": 443,
+                "protocol": "TCP"
+              }
+            ]
+          },
+          {
+            "cpu": "0.5",
+            "image": "microsoft/aci-tutorial-sidecar",
+            "memory": "1.5",
+            "name": "sidecar"
+          }
+        ],
+        "dns_name_label": "aci-label",
+        "ip_address_type": "public",
+        "location": "${azurerm_resource_group.example.location}",
+        "name": "example-continst",
+        "os_type": "Linux",
+        "resource_group_name": "${azurerm_resource_group.example.name}",
+        "tags": {
+          "environment": "testing"
+        }
+      },
+      "skip_rules": null,
+      "max_severity": "",
+      "min_severity": "",
+      "container_images": [
+        {
+          "name": "hello-world",
+          "image": "microsoft/aci-helloworld:latest"
+        },
+        {
+          "name": "sidecar",
+          "image": "microsoft/aci-tutorial-sidecar"
+        }
+      ]
+    }
+  ],
+  "kubernetes_cron_job": [
+    {
+      "id": "kubernetes_cron_job.demo",
+      "name": "demo",
+      "module_name": "root",
+      "source": "terraform-k8s-provider/cron_job.tf",
+      "plan_root": "terraform-k8s-provider",
+      "line": 1,
+      "type": "kubernetes_cron_job",
+      "config": {
+        "metadata": [
+          {
+            "name": "demo"
+          }
+        ],
+        "spec": [
+          {
+            "concurrency_policy": "Replace",
+            "failed_jobs_history_limit": 5,
+            "job_template": [
+              {
+                "metadata": [
+                  {}
+                ],
+                "spec": [
+                  {
+                    "backoff_limit": 2,
+                    "template": [
+                      {
+                        "metadata": [
+                          {}
+                        ],
+                        "spec": [
+                          {
+                            "container": [
+                              {
+                                "command": [
+                                  "/bin/sh",
+                                  "-c",
+                                  "date; echo Hello from the Kubernetes cluster"
+                                ],
+                                "image": "busybox",
+                                "name": "hello"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ],
+                    "ttl_seconds_after_finished": 10
+                  }
+                ]
+              }
+            ],
+            "schedule": "1 0 * * *",
+            "starting_deadline_seconds": 10,
+            "successful_jobs_history_limit": 10
+          }
+        ]
+      },
+      "skip_rules": null,
+      "max_severity": "",
+      "min_severity": "",
+      "container_images": [
+        {
+          "name": "hello",
+          "image": "busybox"
+        }
+      ]
+    }
+  ],
+  "kubernetes_deployment": [
+    {
+      "id": "kubernetes_deployment.example",
+      "name": "example",
+      "module_name": "root",
+      "source": "terraform-k8s-provider/deployment.tf",
+      "plan_root": "terraform-k8s-provider",
+      "line": 37,
+      "type": "kubernetes_deployment",
+      "config": {
+        "metadata": [
+          {
+            "labels": {
+              "test": "MyExampleApp"
+            },
+            "name": "terraform-example",
+            "namespace": "${kubernetes_namespace.test.metadata.0.name}"
+          }
+        ],
+        "spec": [
+          {
+            "replicas": 1,
+            "selector": [
+              {
+                "match_labels": {
+                  "test": "MyExampleApp"
+                }
+              }
+            ],
+            "template": [
+              {
+                "metadata": [
+                  {
+                    "labels": {
+                      "test": "MyExampleApp"
+                    }
+                  }
+                ],
+                "spec": [
+                  {
+                    "automount_service_account_token": false,
+                    "container": [
+                      {
+                        "image": "nginx:1.7.8",
+                        "name": "example1"
+                      },
+                      {
+                        "image": "nginx:1.7.8",
+                        "name": "example2"
+                      },
+                      {
+                        "image": "nginx:1.7.8",
+                        "name": "example3"
+                      }
+                    ],
+                    "service_account_name": "default"
+                  }
+                ]
+              }
+            ]
+          }
+        ]
+      },
+      "skip_rules": null,
+      "max_severity": "",
+      "min_severity": "",
+      "container_images": [
+        {
+          "name": "example1",
+          "image": "nginx:1.7.8"
+        },
+        {
+          "name": "example2",
+          "image": "nginx:1.7.8"
+        },
+        {
+          "name": "example3",
+          "image": "nginx:1.7.8"
+        }
+      ]
+    }
+  ],
+  "kubernetes_job": [
+    {
+      "id": "kubernetes_job.demo",
+      "name": "demo",
+      "module_name": "root",
+      "source": "terraform-k8s-provider/job.tf",
+      "plan_root": "terraform-k8s-provider",
+      "line": 1,
+      "type": "kubernetes_job",
+      "config": {
+        "metadata": [
+          {
+            "name": "demo"
+          }
+        ],
+        "spec": [
+          {
+            "backoff_limit": 4,
+            "template": [
+              {
+                "metadata": [
+                  {}
+                ],
+                "spec": [
+                  {
+                    "container": [
+                      {
+                        "command": [
+                          "perl",
+                          "-Mbignum=bpi",
+                          "-wle",
+                          "print bpi(2000)"
+                        ],
+                        "image": "perl",
+                        "name": "pi"
+                      }
+                    ],
+                    "restart_policy": "Never"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "wait_for_completion": true
+      },
+      "skip_rules": null,
+      "max_severity": "",
+      "min_severity": "",
+      "container_images": [
+        {
+          "name": "pi",
+          "image": "perl"
+        }
+      ]
+    }
+  ],
+  "kubernetes_namespace": [
+    {
+      "id": "kubernetes_namespace.test",
+      "name": "test",
+      "module_name": "root",
+      "source": "terraform-k8s-provider/deployment.tf",
+      "plan_root": "terraform-k8s-provider",
+      "line": 13,
+      "type": "kubernetes_namespace",
+      "config": {
+        "metadata": [
+          {
+            "name": "nginx"
+          }
+        ]
+      },
+      "skip_rules": null,
+      "max_severity": "",
+      "min_severity": ""
+    }
+  ],
+  "kubernetes_pod": [
+    {
+      "id": "kubernetes_pod.test",
+      "name": "test",
+      "module_name": "root",
+      "source": "terraform-k8s-provider/pod.tf",
+      "plan_root": "terraform-k8s-provider",
+      "line": 1,
+      "type": "kubernetes_pod",
+      "config": {
+        "metadata": [
+          {
+            "name": "terraform-example"
+          }
+        ],
+        "spec": [
+          {
+            "container": [
+              {
+                "image": "nginx:1.7.9",
+                "name": "example",
+                "resources": [
+                  {
+                    "limits": {
+                      "cpu": "0.5",
+                      "memory": "512Mi",
+                      "nvidia/gpu": "1"
+                    },
+                    "requests": {
+                      "cpu": "250m",
+                      "memory": "50Mi",
+                      "nvidia/gpu": "1"
+                    }
+                  }
+                ]
+              }
+            ]
+          }
+        ]
+      },
+      "skip_rules": null,
+      "max_severity": "",
+      "min_severity": "",
+      "container_images": [
+        {
+          "name": "example",
+          "image": "nginx:1.7.9"
+        }
+      ]
+    }
+  ],
+  "kubernetes_replication_controller": [
+    {
+      "id": "kubernetes_replication_controller.example",
+      "name": "example",
+      "module_name": "root",
+      "source": "terraform-k8s-provider/rc.tf",
+      "plan_root": "terraform-k8s-provider",
+      "line": 1,
+      "type": "kubernetes_replication_controller",
+      "config": {
+        "metadata": [
+          {
+            "labels": {
+              "test": "MyExampleApp"
+            },
+            "name": "terraform-example"
+          }
+        ],
+        "spec": [
+          {
+            "selector": {
+              "test": "MyExampleApp"
+            },
+            "template": [
+              {
+                "metadata": [
+                  {
+                    "annotations": {
+                      "key1": "value1"
+                    },
+                    "labels": {
+                      "test": "MyExampleApp"
+                    }
+                  }
+                ],
+                "spec": [
+                  {
+                    "container": [
+                      {
+                        "image": "nginx:1.7.8",
+                        "liveness_probe": [
+                          {
+                            "http_get": [
+                              {
+                                "http_header": [
+                                  {
+                                    "name": "X-Custom-Header",
+                                    "value": "Awesome"
+                                  }
+                                ],
+                                "path": "/nginx_status",
+                                "port": 8080
+                              }
+                            ],
+                            "initial_delay_seconds": 3,
+                            "period_seconds": 3
+                          }
+                        ],
+                        "name": "example",
+                        "resources": [
+                          {
+                            "limits": {
+                              "cpu": "0.5",
+                              "memory": "512Mi"
+                            },
+                            "requests": {
+                              "cpu": "250m",
+                              "memory": "50Mi"
+                            }
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ]
+      },
+      "skip_rules": null,
+      "max_severity": "",
+      "min_severity": "",
+      "container_images": [
+        {
+          "name": "example",
+          "image": "nginx:1.7.8"
+        }
+      ]
+    }
+  ],
+  "kubernetes_service": [
+    {
+      "id": "kubernetes_service.test",
+      "name": "test",
+      "module_name": "root",
+      "source": "terraform-k8s-provider/deployment.tf",
+      "plan_root": "terraform-k8s-provider",
+      "line": 19,
+      "type": "kubernetes_service",
+      "config": {
+        "metadata": [
+          {
+            "name": "nginx",
+            "namespace": "${kubernetes_namespace.test.metadata.0.name}"
+          }
+        ],
+        "spec": [
+          {
+            "port": [
+              {
+                "node_port": 30201,
+                "port": 80,
+                "target_port": 80
+              }
+            ],
+            "selector": {
+              "app": "${kubernetes_deployment.example.spec.0.template.0.metadata.0.labels.test}"
+            },
+            "type": "NodePort"
+          }
+        ]
+      },
+      "skip_rules": null,
+      "max_severity": "",
+      "min_severity": ""
+    }
+  ],
+  "kubernetes_stateful_set": [
+    {
+      "id": "kubernetes_stateful_set.prometheus",
+      "name": "prometheus",
+      "module_name": "root",
+      "source": "terraform-k8s-provider/stateful_set.tf",
+      "plan_root": "terraform-k8s-provider",
+      "line": 1,
+      "type": "kubernetes_stateful_set",
+      "config": {
+        "metadata": [
+          {
+            "annotations": {
+              "SomeAnnotation": "foobar"
+            },
+            "labels": {
+              "addonmanager.kubernetes.io/mode": "Reconcile",
+              "k8s-app": "prometheus",
+              "kubernetes.io/cluster-service": "true",
+              "version": "v2.2.1"
+            },
+            "name": "prometheus"
+          }
+        ],
+        "spec": [
+          {
+            "pod_management_policy": "Parallel",
+            "replicas": 1,
+            "revision_history_limit": 5,
+            "selector": [
+              {
+                "match_labels": {
+                  "k8s-app": "prometheus"
+                }
+              }
+            ],
+            "service_name": "prometheus",
+            "template": [
+              {
+                "metadata": [
+                  {
+                    "annotations": {},
+                    "labels": {
+                      "k8s-app": "prometheus"
+                    }
+                  }
+                ],
+                "spec": [
+                  {
+                    "container": [
+                      {
+                        "args": [
+                          "--volume-dir=/etc/config",
+                          "--webhook-url=http://localhost:9090/-/reload"
+                        ],
+                        "image": "jimmidyson/configmap-reload:v0.1",
+                        "image_pull_policy": "IfNotPresent",
+                        "name": "prometheus-server-configmap-reload",
+                        "resources": [
+                          {
+                            "limits": {
+                              "cpu": "10m",
+                              "memory": "10Mi"
+                            },
+                            "requests": {
+                              "cpu": "10m",
+                              "memory": "10Mi"
+                            }
+                          }
+                        ],
+                        "volume_mount": [
+                          {
+                            "mount_path": "/etc/config",
+                            "name": "config-volume",
+                            "read_only": true
+                          }
+                        ]
+                      },
+                      {
+                        "args": [
+                          "--config.file=/etc/config/prometheus.yml",
+                          "--storage.tsdb.path=/data",
+                          "--web.console.libraries=/etc/prometheus/console_libraries",
+                          "--web.console.templates=/etc/prometheus/consoles",
+                          "--web.enable-lifecycle"
+                        ],
+                        "image": "prom/prometheus:v2.2.1",
+                        "image_pull_policy": "IfNotPresent",
+                        "liveness_probe": [
+                          {
+                            "http_get": [
+                              {
+                                "path": "/-/healthy",
+                                "port": 9090,
+                                "scheme": "HTTPS"
+                              }
+                            ],
+                            "initial_delay_seconds": 30,
+                            "timeout_seconds": 30
+                          }
+                        ],
+                        "name": "prometheus-server",
+                        "port": [
+                          {
+                            "container_port": 9090
+                          }
+                        ],
+                        "readiness_probe": [
+                          {
+                            "http_get": [
+                              {
+                                "path": "/-/ready",
+                                "port": 9090
+                              }
+                            ],
+                            "initial_delay_seconds": 30,
+                            "timeout_seconds": 30
+                          }
+                        ],
+                        "resources": [
+                          {
+                            "limits": {
+                              "cpu": "200m",
+                              "memory": "1000Mi"
+                            },
+                            "requests": {
+                              "cpu": "200m",
+                              "memory": "1000Mi"
+                            }
+                          }
+                        ],
+                        "volume_mount": [
+                          {
+                            "mount_path": "/etc/config",
+                            "name": "config-volume"
+                          },
+                          {
+                            "mount_path": "/data",
+                            "name": "prometheus-data",
+                            "sub_path": ""
+                          }
+                        ]
+                      }
+                    ],
+                    "init_container": [
+                      {
+                        "command": [
+                          "chown",
+                          "-R",
+                          "65534:65534",
+                          "/data"
+                        ],
+                        "image": "busybox:latest",
+                        "image_pull_policy": "IfNotPresent",
+                        "name": "init-chown-data",
+                        "volume_mount": [
+                          {
+                            "mount_path": "/data",
+                            "name": "prometheus-data",
+                            "sub_path": ""
+                          }
+                        ]
+                      }
+                    ],
+                    "service_account_name": "prometheus",
+                    "termination_grace_period_seconds": 300,
+                    "volume": [
+                      {
+                        "config_map": [
+                          {
+                            "name": "prometheus-config"
+                          }
+                        ],
+                        "name": "config-volume"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ],
+            "update_strategy": [
+              {
+                "rolling_update": [
+                  {
+                    "partition": 1
+                  }
+                ],
+                "type": "RollingUpdate"
+              }
+            ],
+            "volume_claim_template": [
+              {
+                "metadata": [
+                  {
+                    "name": "prometheus-data"
+                  }
+                ],
+                "spec": [
+                  {
+                    "access_modes": [
+                      "ReadWriteOnce"
+                    ],
+                    "resources": [
+                      {
+                        "requests": {
+                          "storage": "16Gi"
+                        }
+                      }
+                    ],
+                    "storage_class_name": "standard"
+                  }
+                ]
+              }
+            ]
+          }
+        ]
+      },
+      "skip_rules": null,
+      "max_severity": "",
+      "min_severity": "",
+      "container_images": [
+        {
+          "name": "prometheus-server-configmap-reload",
+          "image": "jimmidyson/configmap-reload:v0.1"
+        },
+        {
+          "name": "prometheus-server",
+          "image": "prom/prometheus:v2.2.1"
+        }
+      ],
+      "init_container_images": [
+        {
+          "name": "init-chown-data",
+          "image": "busybox:latest"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file