Repository: klothoplatform/klotho
Pull request #837 (2023-12-20 17:05:49)

Expand visualizer children to include more resources
In draft because this will require backend changes to support the new structure (yaml lists instead of comma-separated strings)


Patch:
diff --git a/pkg/construct2/dot.go b/pkg/construct2/dot.go
index 2a395553..ec63fd33 100644
--- a/pkg/construct2/dot.go
+++ b/pkg/construct2/dot.go
@@ -29,11 +29,18 @@ func dotEdgeAttributes(e ResourceEdge) map[string]string {
 		}
 		return nil
 	})
+	if e.Properties.Weight > 0 {
+		if a["label"] == "" {
+			a["label"] = fmt.Sprintf("%d", e.Properties.Weight)
+		} else {
+			a["label"] = fmt.Sprintf("%s\n%d", a["label"], e.Properties.Weight)
+		}
+	}
 	return a
 }
 
 func GraphToDOT(g Graph, out io.Writer) error {
-	ids, err := ToplogicalSort(g)
+	ids, err := TopologicalSort(g)
 	if err != nil {
 		return err
 	}
diff --git a/pkg/construct2/graph.go b/pkg/construct2/graph.go
index e669ac88..58e6bc1f 100644
--- a/pkg/construct2/graph.go
+++ b/pkg/construct2/graph.go
@@ -17,14 +17,19 @@ type (
 	ResourceEdge = graph.Edge[*Resource]
 )
 
-func NewGraph(options ...func(*graph.Traits)) Graph {
-	return Graph(graph.NewWithStore(
+func NewGraphWithOptions(options ...func(*graph.Traits)) Graph {
+	return graph.NewWithStore(
 		ResourceHasher,
 		graph_addons.NewMemoryStore[ResourceId, *Resource](),
-		append(options,
-			graph.Directed(),
-		)...,
-	))
+		options...,
+	)
+}
+
+func NewGraph(options ...func(*graph.Traits)) Graph {
+	return NewGraphWithOptions(append(options,
+		graph.Directed(),
+	)...,
+	)
 }
 
 func NewAcyclicGraph(options ...func(*graph.Traits)) Graph {
@@ -48,7 +53,7 @@ func String(g Graph) (string, error) {
 }
 
 func stringTo(g Graph, w io.Writer) error {
-	topo, err := ToplogicalSort(g)
+	topo, err := TopologicalSort(g)
 	if err != nil {
 		return err
 	}
diff --git a/pkg/construct2/graph_io.go b/pkg/construct2/graph_io.go
index 0b8b90e5..2391e1b7 100644
--- a/pkg/construct2/graph_io.go
+++ b/pkg/construct2/graph_io.go
@@ -25,7 +25,7 @@ var nullNode = &yaml.Node{
 }
 
 func (g YamlGraph) MarshalYAML() (interface{}, error) {
-	topo, err := ToplogicalSort(g.Graph)
+	topo, err := TopologicalSort(g.Graph)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/construct2/graph_vertices.go b/pkg/construct2/graph_vertices.go
index c90d4797..271949e1 100644
--- a/pkg/construct2/graph_vertices.go
+++ b/pkg/construct2/graph_vertices.go
@@ -3,17 +3,31 @@ package construct2
 import (
 	"errors"
 	"fmt"
+	"slices"
 	"sort"
+
+	"github.com/dominikbraun/graph"
 )
 
-// ToplogicalSort provides a stable topological ordering of resource IDs.
+// TopologicalSort provides a stable topological ordering of resource IDs.
 // This is a modified implementation of graph.StableTopologicalSort with the primary difference
 // being any uses of the internal function `enqueueArbitrary`.
-func ToplogicalSort(g Graph) ([]ResourceId, error) {
+func TopologicalSort[T any](g graph.Graph[ResourceId, T]) ([]ResourceId, error) {
 	return toplogicalSort(g, false)
 }
 
-func toplogicalSort(g Graph, invertLess bool) ([]ResourceId, error) {
+// ReverseTopologicalSort is like TopologicalSort, but returns the reverse order. This is primarily useful for
+// IaC graphs to determine the order in which resources should be created.
+func ReverseTopologicalSort[T any](g graph.Graph[ResourceId, T]) ([]ResourceId, error) {
+	topo, err := toplogicalSort(g, true)
+	if err != nil {
+		return nil, err
+	}
+	slices.Reverse(topo)
+	return topo, nil
+}
+
+func toplogicalSort[T any](g graph.Graph[ResourceId, T], invertLess bool) ([]ResourceId, error) {
 	if !g.Traits().IsDirected {
 		return nil, fmt.Errorf("topological sort cannot be computed on undirected graph")
 	}
@@ -123,23 +137,6 @@ func toplogicalSort(g Graph, invertLess bool) ([]ResourceId, error) {
 	return order, nil
 }
 
-func reverseInplace[E any](a []E) {
-	for i := 0; i < len(a)/2; i++ {
-		a[i], a[len(a)-i-1] = a[len(a)-i-1], a[i]
-	}
-}
-
-// ReverseTopologicalSort is like TopologicalSort, but returns the reverse order. This is primarily useful for
-// IaC graphs to determine the order in which resources should be created.
-func ReverseTopologicalSort(g Graph) ([]ResourceId, error) {
-	topo, err := toplogicalSort(g, true)
-	if err != nil {
-		return nil, err
-	}
-	reverseInplace(topo)
-	return topo, nil
-}
-
 // WalkGraphFunc is much like `fs.WalkDirFunc` and is used in `WalkGraph` and `WalkGraphReverse` for the callback
 // during graph traversal. Return `StopWalk` to end the walk.
 type WalkGraphFunc func(id ResourceId, resource *Resource, nerr error) error
@@ -164,7 +161,7 @@ func walkGraph(g Graph, ids []ResourceId, fn WalkGraphFunc) (nerr error) {
 }
 
 func WalkGraph(g Graph, fn WalkGraphFunc) error {
-	topo, err := ToplogicalSort(g)
+	topo, err := TopologicalSort(g)
 	if err != nil {
 		return err
 	}
diff --git a/pkg/construct2/paths.go b/pkg/construct2/paths.go
index 5ab9f1b7..dc2a3832 100644
--- a/pkg/construct2/paths.go
+++ b/pkg/construct2/paths.go
@@ -7,11 +7,12 @@ import (
 	"strings"
 
 	"github.com/dominikbraun/graph"
+	"github.com/klothoplatform/klotho/pkg/graph_addons"
 	"github.com/klothoplatform/klotho/pkg/set"
 )
 
 type (
-	Path []ResourceId
+	Path graph_addons.Path[ResourceId]
 
 	Dependencies struct {
 		Resource ResourceId
diff --git a/pkg/engine2/cli.go b/pkg/engine2/cli.go
index 2bf51571..64b773ac 100644
--- a/pkg/engine2/cli.go
+++ b/pkg/engine2/cli.go
@@ -181,6 +181,7 @@ func addSubProperties(properties map[string]any, subProperties map[string]knowle
 			"configurationDisabled": details.ConfigurationDisabled,
 			"required":              details.Required,
 			"description":           details.Description,
+			"important":             details.IsImportant,
 		}
 		for _, validationField := range validationFields {
 			valField := reflect.ValueOf(subProperty).Elem().FieldByName(validationField)
@@ -233,25 +234,30 @@ func (em *EngineMain) ListAttributes(cmd *cobra.Command, args []string) error {
 	return nil
 }
 
-func (em *EngineMain) RunEngine(cmd *cobra.Command, args []string) error {
+func setupProfiling() func() {
 	if engineCfg.profileTo != "" {
 		err := os.MkdirAll(filepath.Dir(engineCfg.profileTo), 0755)
 		if err != nil {
-			return fmt.Errorf("failed to create profile directory: %w", err)
+			panic(fmt.Errorf("failed to create profile directory: %w", err))
 		}
 		profileF, err := os.OpenFile(engineCfg.profileTo, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
 		if err != nil {
-			return fmt.Errorf("failed to open profile file: %w", err)
+			panic(fmt.Errorf("failed to open profile file: %w", err))
 		}
-		defer func() {
-			pprof.StopCPUProfile()
-			profileF.Close()
-		}()
 		err = pprof.StartCPUProfile(profileF)
 		if err != nil {
-			return fmt.Errorf("failed to start profile: %w", err)
+			panic(fmt.Errorf("failed to start profile: %w", err))
+		}
+		return func() {
+			pprof.StopCPUProfile()
+			profileF.Close()
 		}
 	}
+	return func() {}
+}
+
+func (em *EngineMain) RunEngine(cmd *cobra.Command, args []string) error {
+	defer setupProfiling()()
 
 	// Set up analytics, and hook them up to the logs
 	analyticsClient := analytics.NewClient()
@@ -345,24 +351,7 @@ func (em *EngineMain) RunEngine(cmd *cobra.Command, args []string) error {
 }
 
 func (em *EngineMain) GetValidEdgeTargets(cmd *cobra.Command, args []string) error {
-	if engineCfg.profileTo != "" {
-		err := os.MkdirAll(filepath.Dir(engineCfg.profileTo), 0755)
-		if err != nil {
-			return fmt.Errorf("failed to create profile directory: %w", err)
-		}
-		profileF, err := os.OpenFile(engineCfg.profileTo, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
-		if err != nil {
-			return fmt.Errorf("failed to open profile file: %w", err)
-		}
-		defer func() {
-			pprof.StopCPUProfile()
-			profileF.Close()
-		}()
-		err = pprof.StartCPUProfile(profileF)
-		if err != nil {
-			return fmt.Errorf("failed to start profile: %w", err)
-		}
-	}
+	defer setupProfiling()()
 
 	// Set up analytics, and hook them up to the logs
 	analyticsClient := analytics.NewClient()
diff --git a/pkg/engine2/edge_targets.go b/pkg/engine2/edge_targets.go
index 11f7e90a..68886a94 100644
--- a/pkg/engine2/edge_targets.go
+++ b/pkg/engine2/edge_targets.go
@@ -1,6 +1,11 @@
 package engine2
 
 import (
+	"os"
+	"slices"
+	"sync"
+	"time"
+
 	"github.com/alitto/pond"
 	"github.com/dominikbraun/graph"
 	construct "github.com/klothoplatform/klotho/pkg/construct2"
@@ -8,10 +13,6 @@ import (
 	"github.com/klothoplatform/klotho/pkg/set"
 	"go.uber.org/zap"
 	"gopkg.in/yaml.v3"
-	"os"
-	"slices"
-	"sync"
-	"time"
 )
 
 type (
@@ -127,7 +128,6 @@ that satisfies both source and target path satisfaction classifications.
 A partial set of valid targets can be generated using the filter criteria in the context's config.
 */
 func (e *Engine) GetValidEdgeTargets(context *GetPossibleEdgesContext) (map[string][]string, error) {
-
 	inputGraph, err := unmarshallInputGraph(context.InputGraph)
 	if err != nil {
 		return nil, err
@@ -153,10 +153,14 @@ func (e *Engine) GetValidEdgeTargets(context *GetPossibleEdgesContext) (map[stri
 	}
 
 	// filter resources based on the context
-	err = construct.WalkGraph(topologyGraph, func(id construct.ResourceId, resource *construct.Resource, nerr error) error {
-		tag := e.GetResourceVizTag(string(DataflowView), id)
+	ids, err := construct.TopologicalSort(topologyGraph)
+	if err != nil {
+		return nil, err
+	}
+	for _, id := range ids {
+		tag := GetResourceVizTag(e.Kb, DataflowView, id)
 		if len(context.Tags) > 0 && !slices.Contains(context.Tags, tag) {
-			return nil
+			continue
 		}
 		isSource := true
 		isTarget := true
@@ -181,11 +185,6 @@ func (e *Engine) GetValidEdgeTargets(context *GetPossibleEdgesContext) (map[stri
 		if isTarget {
 			targets = append(targets, id)
 		}
-
-		return nil
-	})
-	if err != nil {
-		return nil, err
 	}
 
 	results := make(chan *edgeValidity)
diff --git a/pkg/engine2/operational_eval/vertex_path_expand.go b/pkg/engine2/operational_eval/vertex_path_expand.go
index 4a935642..a81a877a 100644
--- a/pkg/engine2/operational_eval/vertex_path_expand.go
+++ b/pkg/engine2/operational_eval/vertex_path_expand.go
@@ -401,7 +401,7 @@ func (v *pathExpandVertex) addDepsFromEdge(
 		return nil
 	}
 
-	allRes, err := construct.ToplogicalSort(eval.Solution.RawView())
+	allRes, err := construct.TopologicalSort(eval.Solution.RawView())
 	if err != nil {
 		return err
 	}
diff --git a/pkg/engine2/operational_rule/operational_action.go b/pkg/engine2/operational_rule/operational_action.go
index d88fda2c..1913b26f 100644
--- a/pkg/engine2/operational_rule/operational_action.go
+++ b/pkg/engine2/operational_rule/operational_action.go
@@ -127,7 +127,7 @@ func (action *operationalResourceAction) useAvailableResources(resource *constru
 	if err != nil {
 		return err
 	}
-	resources, err := construct.ToplogicalSort(action.ruleCtx.Solution.RawView())
+	resources, err := construct.TopologicalSort(action.ruleCtx.Solution.RawView())
 	if err != nil {
 		return err
 	}
@@ -409,7 +409,7 @@ func (action *operationalResourceAction) generateResourceName(resourceToSet *con
 
 func generateResourceName(sol solution_context.SolutionContext, resourceToSet *construct.ResourceId, resource construct.ResourceId) error {
 	numResources := 0
-	ids, err := construct.ToplogicalSort(sol.DataflowGraph())
+	ids, err := construct.TopologicalSort(sol.DataflowGraph())
 	if err != nil {
 		return err
 	}
diff --git a/pkg/engine2/path_selection/path_expansion.go b/pkg/engine2/path_selection/path_expansion.go
index 889e6ad0..7e4ada17 100644
--- a/pkg/engine2/path_selection/path_expansion.go
+++ b/pkg/engine2/path_selection/path_expansion.go
@@ -178,7 +178,7 @@ func renameAndReplaceInTempGraph(
 
 func getCurrNames(sol solution_context.SolutionContext, resourceToSet *construct.ResourceId) (set.Set[string], error) {
 	currNames := make(set.Set[string])
-	ids, err := construct.ToplogicalSort(sol.DataflowGraph())
+	ids, err := construct.TopologicalSort(sol.DataflowGraph())
 	if err != nil {
 		return currNames, err
 	}
diff --git a/pkg/engine2/visualizer.go b/pkg/engine2/visualizer.go
index 6b077fe3..93b1cc2d 100644
--- a/pkg/engine2/visualizer.go
+++ b/pkg/engine2/visualizer.go
@@ -3,19 +3,17 @@ package engine2
 import (
 	"errors"
 	"fmt"
-	"sort"
-	"strings"
+	"math"
 
 	"github.com/dominikbraun/graph"
-	"github.com/klothoplatform/klotho/pkg/collectionutil"
 	construct "github.com/klothoplatform/klotho/pkg/construct2"
 	"github.com/klothoplatform/klotho/pkg/engine2/path_selection"
 	"github.com/klothoplatform/klotho/pkg/engine2/solution_context"
+	"github.com/klothoplatform/klotho/pkg/graph_addons"
 	klotho_io "github.com/klothoplatform/klotho/pkg/io"
 	knowledgebase "github.com/klothoplatform/klotho/pkg/knowledge_base2"
 	"github.com/klothoplatform/klotho/pkg/set"
 	visualizer "github.com/klothoplatform/klotho/pkg/visualizer2"
-	"go.uber.org/zap"
 )
 
 type (
@@ -33,253 +31,273 @@ const (
 	NoRenderTag   Tag = "no-render"
 )
 
-type (
-	TopologyNode struct {
-		Resource construct.ResourceId
-		Parent   construct.ResourceId
-		Children set.Set[construct.ResourceId]
-	}
-)
-type Topology struct {
-	Nodes map[string]*TopologyNode
-	Edges map[construct.SimpleEdge]construct.Path
-}
-
 func (e *Engine) VisualizeViews(ctx solution_context.SolutionContext) ([]klotho_io.File, error) {
 	iac_topo := &visualizer.File{
 		FilenamePrefix: "iac-",
 		Provider:       "aws",
-		DAG:            ctx.DeploymentGraph(),
 	}
 	dataflow_topo := &visualizer.File{
 		FilenamePrefix: "dataflow-",
 		Provider:       "aws",
 	}
 	var err error
-	dataflow_topo.DAG, err = e.GetViewsDag(DataflowView, ctx)
+	iac_topo.Graph, err = visualizer.ConstructToVis(ctx.DeploymentGraph())
+	if err != nil {
+		return nil, err
+	}
+	dataflow_topo.Graph, err = e.GetViewsDag(DataflowView, ctx)
 	return []klotho_io.File{iac_topo, dataflow_topo}, err
 }
 
-func (e *Engine) GetResourceVizTag(view string, resource construct.ResourceId) Tag {
-	template, err := e.Kb.GetResourceTemplate(resource)
+func GetResourceVizTag(kb knowledgebase.TemplateKB, view View, resource construct.ResourceId) Tag {
+	template, err := kb.GetResourceTemplate(resource)
 
 	if template == nil || err != nil {
 		return NoRenderTag
 	}
-	tag, found := template.Views[view]
+	tag, found := template.Views[string(view)]
 	if !found {
 		return NoRenderTag
 	}
 	return Tag(tag)
 }
 
-func (e *Engine) GetViewsDag(view View, ctx solution_context.SolutionContext) (construct.Graph, error) {
-	topo := Topology{
-		Nodes: make(map[string]*TopologyNode),
-		Edges: make(map[construct.SimpleEdge]construct.Path),
+func (e *Engine) GetViewsDag(view View, sol solution_context.SolutionContext) (visualizer.VisGraph, error) {
+	viewDag := visualizer.NewVisGraph()
+	var graph construct.Graph
+	if view == IACView {
+		graph = sol.DeploymentGraph()
+	} else {
+		graph = sol.DataflowGraph()
 	}
-	viewDag := construct.NewGraph()
-	df := ctx.DataflowGraph()
 
-	resources, err := construct.ReverseTopologicalSort(df)
+	undirected := construct.NewGraphWithOptions()
+	err := undirected.AddVerticesFrom(graph)
+	if err != nil {
+		return nil, fmt.Errorf("could not copy vertices for undirected: %w", err)
+	}
+	err = undirected.AddEdgesFrom(graph)
+	if err != nil {
+		return nil, fmt.Errorf("could not copy edges for undirected: %w", err)
+	}
+
+	ids, err := construct.ReverseTopologicalSort(graph)
 	if err != nil {
 		return nil, err
 	}
+
 	var errs error
-	for _, src := range resources {
-		node := &TopologyNode{
-			Resource: src,
-			Children: make(set.Set[construct.ResourceId]),
-			Parent:   e.getParentFromNamespace(src, resources),
-		}
 
-		tag := e.GetResourceVizTag(string(DataflowView), src)
-		switch tag {
-		case ParentIconTag, BigIconTag:
-			topo.Nodes[src.String()] = node
-		case SmallIconTag, NoRenderTag:
-			continue
-		default:
-			errs = errors.Join(errs, fmt.Errorf("unknown tag %s, for resource %s", tag, src))
+	// First pass gets all the vertices (groups or big icons)
+	for _, id := range ids {
+		var err error
+		switch tag := GetResourceVizTag(e.Kb, view, id); tag {
+		case NoRenderTag:
 			continue
+		case ParentIconTag, BigIconTag:
+			err = viewDag.AddVertex(&visualizer.VisResource{
+				ID:       id,
+				Children: make(set.Set[construct.ResourceId]),
+				Tag:      string(tag),
+			})
 		}
+		errs = errors.Join(errs, err)
+	}
+	if errs != nil {
+		return nil, errs
+	}
 
-		deps, err := construct.DownstreamDependencies(
-			df,
-			src,
-			knowledgebase.DependenciesSkipEdgeLayer(df, e.Kb, src, knowledgebase.FirstFunctionalLayer),
-		)
-		if err != nil {
-			errs = errors.Join(errs, err)
+	// Second pass sets up the small icons & edges between big icons
+	for _, id := range ids {
+		switch tag := GetResourceVizTag(e.Kb, view, id); tag {
+		case NoRenderTag:
 			continue
-		}
-		zap.S().Debugf("%s paths: %d", src, len(deps.Paths))
-		sort.Slice(deps.Paths, func(i, j int) bool {
-			li, lj := len(deps.Paths[i]), len(deps.Paths[j])
-			if li != lj {
-				return li < lj
-			}
-			for pathIdx := 0; pathIdx < li; pathIdx++ {
-				resI := deps.Paths[i][pathIdx]
-				resJ := deps.Paths[j][pathIdx]
-				if resI != resJ {
-					return construct.ResourceIdLess(resI, resJ)
-				}
-			}
-			return false
-		})
-		seenSmall := make(set.Set[construct.ResourceId])
-		for _, path := range deps.Paths {
-			dst := path[len(path)-1]
-			if dst == src {
-				continue
+		case ParentIconTag:
+			continue
+		case BigIconTag:
+			err := e.handleBigIcon(sol, view, undirected, viewDag, id)
+			if err != nil {
+				errs = errors.Join(errs, fmt.Errorf("failed to handle big icon %s: %w", id, err))
 			}
-			dstTag := e.GetResourceVizTag(string(DataflowView), dst)
-			switch dstTag {
-			case ParentIconTag:
-				if !node.Parent.IsZero() {
-					continue
-				}
-				template, err := e.Kb.GetResourceTemplate(dst)
-				if err != nil {
-					errs = errors.Join(errs, err)
-					continue
-				}
-				if collectionutil.Contains(template.Classification.Is, "cluster") ||
-					collectionutil.Contains(template.Classification.Is, "network") {
-					hasPath, err := HasParent(topo, ctx, src, dst)
-					if err != nil {
-						errs = errors.Join(errs, err)
-					}
-					if node.Parent.IsZero() && hasPath {
-						node.Parent = dst
-					}
-				} else {
-					errs = errors.Join(errs, createEdgeIfPath(topo, ctx, src, dst, path))
-				}
-			case BigIconTag:
-				errs = errors.Join(errs, createEdgeIfPath(topo, ctx, src, dst, path))
-			case SmallIconTag:
-				if seenSmall.Contains(dst) {
-					continue
-				}
-				seenSmall.Add(dst)
-				isSideEffect, err := knowledgebase.IsOperationalResourceSideEffect(df, ctx.KnowledgeBase(), src, dst)
-				if err != nil {
-					errs = errors.Join(errs, err)
-					continue
-				}
-				if isSideEffect {
-					node.Children.Add(dst)
-				}
-			case NoRenderTag:
-				continue
-			default:
-				errs = errors.Join(errs, fmt.Errorf("unknown tag %s, for resource %s", dstTag, dst))
+		case SmallIconTag:
+			err := e.handleSmallIcon(view, undirected, viewDag, id)
+			if err != nil {
+				errs = errors.Join(errs, fmt.Errorf("failed to handle small icon %s: %w", id, err))
 			}
+		default:
+			errs = errors.Join(errs, fmt.Errorf("unknown tag %s", tag))
 		}
+	}
+
+	return viewDag, errs
+}
 
+// handleSmallIcon finds big icons to attach this resource to. It always adds to big icons for which it is
+// in the glue layer. It also adds to the big icon that is closest to the resource (if there is one).
+func (e *Engine) handleSmallIcon(
+	view View,
+	g construct.Graph,
+	viewDag visualizer.VisGraph,
+	id construct.ResourceId,
+) error {
+	ids, err := construct.TopologicalSort(viewDag)
+	if err != nil {
+		return err
 	}
-	if errs != nil {
-		return nil, errs
+	pather, err := construct.ShortestPaths(g, id, construct.DontSkipEdges)
+	if err != nil {
+		return err
 	}
-
-	for _, node := range topo.Nodes {
-		childrenIds := make([]string, len(node.Children))
-		children := node.Children.ToSlice()
-		sort.Slice(children, func(i, j int) bool {
-			return construct.ResourceIdLess(children[i], children[j])
-		})
-		for i, child := range children {
-			childrenIds[i] = child.String()
-		}
-		properties := map[string]string{}
-		if len(node.Children) > 0 {
-			properties[string(visualizer.ChildrenKey)] = strings.Join(childrenIds, ",")
+	glueIds, err := knowledgebase.Downstream(g, e.Kb, id, knowledgebase.ResourceGlueLayer)
+	if err != nil {
+		return err
+	}
+	glue := set.SetOf(glueIds...)
+	var errs error
+	var bestParent *visualizer.VisResource
+	bestParentWeight := math.MaxInt32
+	for _, candidate := range ids {
+		// If the resource is in the glue layer, add it
+		if glue.Contains(candidate) {
+			parent, err := viewDag.Vertex(candidate)
+			if err != nil {
+				errs = errors.Join(errs, err)
+				continue
+			}
+			parent.Children.Add(id)
 		}
-		if !node.Parent.IsZero() {
-			properties[string(visualizer.ParentKey)] = node.Parent.String()
+
+		// Even if it was in the glue layer, continue calculating the best parent so we don't accidentally
+		// attribute it to a worse parent.
+		path, err := pather.ShortestPath(candidate)
+		if errors.Is(err, graph.ErrTargetNotReachable) {
+			continue
+		} else if err != nil {
+			errs = errors.Join(errs, err)
+			continue
 		}
-		res, err := df.Vertex(node.Resource)
+		weight, err := graph_addons.PathWeight(g, graph_addons.Path[construct.ResourceId](path))
 		if err != nil {
 			errs = errors.Join(errs, err)
 			continue
 		}
-		errs = errors.Join(errs, viewDag.AddVertex(res, graph.VertexAttributes(properties)))
-
-	}
-	if errs != nil {
-		return nil, errs
-	}
-
-	// Remove edges between children and parents
-	for _, node := range topo.Nodes {
-		if !node.Parent.IsZero() {
-			delete(topo.Edges, construct.SimpleEdge{Source: node.Resource, Target: node.Parent})
-			delete(topo.Edges, construct.SimpleEdge{Source: node.Parent, Target: node.Resource})
-		}
-		for edge := range topo.Edges {
-			if edge.Source == node.Parent || edge.Target == node.Parent {
-				delete(topo.Edges, edge)
-				delete(topo.Edges, edge)
+		if weight < bestParentWeight {
+			bestParentWeight = weight
+			bestParent, err = viewDag.Vertex(candidate)
+			if err != nil {
+				errs = errors.Join(errs, err)
+				continue
 			}
 		}
 	}
-
-	for edge, path := range topo.Edges {
-		pathStrings := make([]string, len(path))
-		for i, res := range path {
-			pathStrings[i] = res.String()
-		}
-		data := map[string]interface{}{}
-		if len(path) > 0 {
-			data["path"] = strings.Join(pathStrings, ",")
-		}
-		errs = errors.Join(errs, viewDag.AddEdge(edge.Source, edge.Target, graph.EdgeData(data)))
-	}
-
 	if errs != nil {
-		return nil, errs
+		return errs
 	}
-
-	return viewDag, nil
+	if bestParent != nil {
+		bestParent.Children.Add(id)
+	}
+	return nil
 }
 
-func createEdgeIfPath(topo Topology,
-	ctx solution_context.SolutionContext,
-	src, dst construct.ResourceId,
-	path construct.Path,
+// handleBigIcon sets the parent of the big icon if there is a group it should be added to and
+// adds edges to any other big icons based on having the proper connections (network & permissions).
+func (e *Engine) handleBigIcon(
+	sol solution_context.SolutionContext,
+	view View,
+	undirected construct.Graph,
+	viewDag visualizer.VisGraph,
+	id construct.ResourceId,
 ) error {
-	hasPath, err := HasPath(topo, ctx, src, dst)
+	source, err := viewDag.Vertex(id)
+	if err != nil {
+		return err
+	}
+	parent, err := e.findParent(view, undirected, viewDag, id)
 	if err != nil {
 		return err
 	}
-	if hasPath {
-		edge := construct.SimpleEdge{
-			Source: src,
-			Target: dst,
+	source.Parent = parent
+
+	targets, err := construct.TopologicalSort(viewDag)
+	if err != nil {
+		return err
+	}
+
+	var errs error
+	for _, target := range targets {
+		if target == id {
+			continue
+		}
+		if tag := GetResourceVizTag(e.Kb, view, target); tag != BigIconTag {
+			continue
+		}
+
+		hasPath, err := hasVisPath(sol, view, id, target)
+		if err != nil {
+			errs = errors.Join(errs, err)
+			continue
+		}
+		if hasPath {
+			errs = errors.Join(errs, viewDag.AddEdge(id, target))
 		}
-		topo.Edges[edge] = path[1 : len(path)-1]
 	}
-	return nil
+	return err
 }
 
-func (e *Engine) getParentFromNamespace(resource construct.ResourceId, resources []construct.ResourceId) construct.ResourceId {
-	if resource.Namespace != "" {
-		for _, potentialParent := range resources {
-			if potentialParent.Name == resource.Namespace && e.GetResourceVizTag(string(DataflowView), potentialParent) == ParentIconTag {
-				return potentialParent
+func (e *Engine) findParent(
+	view View,
+	undirected construct.Graph,
+	viewDag visualizer.VisGraph,
+	id construct.ResourceId,
+) (bestParent construct.ResourceId, err error) {
+	ids, err := construct.TopologicalSort(viewDag)
+	if err != nil {
+		return
+	}
+	pather, err := construct.ShortestPaths(undirected, id, construct.DontSkipEdges)
+	if err != nil {
+		return
+	}
+	bestParentWeight := math.MaxInt32
+	var errs error
+candidateLoop:
+	for _, id := range ids {
+		if GetResourceVizTag(e.Kb, view, id) != ParentIconTag {
+			continue
+		}
+		path, err := pather.ShortestPath(id)
+		if errors.Is(err, graph.ErrTargetNotReachable) {
+			continue
+		} else if err != nil {
+			errs = errors.Join(errs, err)
+			continue
+		}
+		for _, pathElem := range path[1 : len(path)-1] {
+			pathTmpl, err := e.Kb.GetResourceTemplate(pathElem)
+			if err != nil {
+				errs = errors.Join(errs, err)
+				continue
 			}
+			// Don't cross functional boundaries for parent attribution
+			if pathTmpl.GetFunctionality() != knowledgebase.Unknown {
+				continue candidateLoop
+			}
+		}
+		weight, err := graph_addons.PathWeight(undirected, graph_addons.Path[construct.ResourceId](path))
+		if err != nil {
+			errs = errors.Join(errs, err)
+			continue
+		}
+		if weight < bestParentWeight {
+			bestParentWeight = weight
+			bestParent = id
 		}
 	}
-	return construct.ResourceId{}
+	err = errs
+	return
 }
 
-func HasParent(topo Topology, sol solution_context.SolutionContext, source, target construct.ResourceId) (bool, error) {
-	return checkPaths(topo, sol, source, target)
-}
-
-func HasPath(topo Topology, sol solution_context.SolutionContext, source, target construct.ResourceId) (bool, error) {
+func hasVisPath(sol solution_context.SolutionContext, view View, source, target construct.ResourceId) (bool, error) {
 	srcTemplate, err := sol.KnowledgeBase().GetResourceTemplate(source)
 	if err != nil || srcTemplate == nil {
 		return false, fmt.Errorf("has path could not find source resource %s: %w", source, err)
@@ -300,25 +318,36 @@ func HasPath(topo Topology, sol solution_context.SolutionContext, source, target
 		return false, fmt.Errorf("has path could not find target resource %s: %w", target, err)
 	}
 
-	consumed, err := knowledgebase.HasConsumedFromResource(sourceRes, targetRes,
-		solution_context.DynamicCtx(sol))
+	consumed, err := knowledgebase.HasConsumedFromResource(
+		sourceRes,
+		targetRes,
+		solution_context.DynamicCtx(sol),
+	)
 	if err != nil {
 		return false, err
 	}
 	if !consumed {
 		return false, nil
 	}
-	return checkPaths(topo, sol, source, target)
+	return checkPaths(sol, view, source, target)
 }
 
-func checkPaths(topo Topology, sol solution_context.SolutionContext, source, target construct.ResourceId) (bool, error) {
-	paths, err := path_selection.GetPaths(sol, source, target, func(source, target construct.ResourceId, path []construct.ResourceId) bool {
-		for i, res := range path {
-			if i < len(path)-1 && (topo.Nodes[res.String()] != nil && res != source && res != target) {
-				return false
+func checkPaths(sol solution_context.SolutionContext, view View, source, target construct.ResourceId) (bool, error) {
+	paths, err := path_selection.GetPaths(
+		sol,
+		source,
+		target,
+		func(source, target construct.ResourceId, path []construct.ResourceId) bool {
+			for _, res := range path[1 : len(path)-1] {
+				switch GetResourceVizTag(sol.KnowledgeBase(), view, res) {
+				case BigIconTag, ParentIconTag:
+					// Don't consider paths that go through big/parent icons
+					return false
+				}
 			}
-		}
-		return true
-	}, true)
+			return true
+		},
+		true,
+	)
 	return len(paths) > 0, err
 }
diff --git a/pkg/graph_addons/layered_graph.go b/pkg/graph_addons/layered_graph.go
index e3f5b966..18109a62 100644
--- a/pkg/graph_addons/layered_graph.go
+++ b/pkg/graph_addons/layered_graph.go
@@ -13,6 +13,10 @@ import (
 // Remove and update operations are applied to all layers.
 type LayeredGraph[K comparable, T any] []graph.Graph[K, T]
 
+func LayeredGraphOf[K comparable, T any](g ...graph.Graph[K, T]) LayeredGraph[K, T] {
+	return g
+}
+
 func (g LayeredGraph[K, T]) Traits() *graph.Traits {
 	t := g[0].Traits()
 	for i := 1; i < len(g); i++ {
diff --git a/pkg/graph_addons/path.go b/pkg/graph_addons/path.go
new file mode 100644
index 00000000..131a56fe
--- /dev/null
+++ b/pkg/graph_addons/path.go
@@ -0,0 +1,25 @@
+package graph_addons
+
+import (
+	"fmt"
+
+	"github.com/dominikbraun/graph"
+)
+
+type Path[K comparable] []K
+
+func PathWeight[K comparable, V any](g graph.Graph[K, V], path Path[K]) (weight int, err error) {
+	if !g.Traits().IsWeighted {
+		return len(path), nil
+	}
+	for i := 1; i < len(path)-1; i++ {
+		var e graph.Edge[V]
+		e, err = g.Edge(path[i-1], path[i])
+		if err != nil {
+			err = fmt.Errorf("edge(path[%d], path[%d]): %w", i-1, i, err)
+			return
+		}
+		weight += e.Properties.Weight
+	}
+	return
+}
diff --git a/pkg/graph_addons/reverse.go b/pkg/graph_addons/reverse.go
new file mode 100644
index 00000000..4a58e376
--- /dev/null
+++ b/pkg/graph_addons/reverse.go
@@ -0,0 +1,38 @@
+package graph_addons
+
+import "github.com/dominikbraun/graph"
+
+func ReverseTopologicalSort[K comparable, T any](g graph.Graph[K, T], less func(K, K) bool) ([]K, error) {
+	reverseLess := func(a, b K) bool {
+		return !less(b, a)
+	}
+	topo, err := graph.StableTopologicalSort(g, reverseLess)
+	if err != nil {
+		return nil, err
+	}
+	for i := 0; i < len(topo)/2; i++ {
+		topo[i], topo[len(topo)-i-1] = topo[len(topo)-i-1], topo[i]
+	}
+	return topo, nil
+}
+
+func ReverseGraph[K comparable, T any](g graph.Graph[K, T]) (graph.Graph[K, T], error) {
+	reverse := graph.NewLike(g)
+	err := reverse.AddVerticesFrom(g)
+	if err != nil {
+		return nil, err
+	}
+	edges, err := g.Edges()
+	if err != nil {
+		return nil, err
+	}
+	for _, e := range edges {
+		err = reverse.AddEdge(e.Target, e.Source, func(ep *graph.EdgeProperties) {
+			*ep = e.Properties
+		})
+		if err != nil {
+			return nil, err
+		}
+	}
+	return reverse, nil
+}
diff --git a/pkg/graph_addons/reverse_topo.go b/pkg/graph_addons/reverse_topo.go
deleted file mode 100644
index 702bec3e..00000000
--- a/pkg/graph_addons/reverse_topo.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package graph_addons
-
-import "github.com/dominikbraun/graph"
-
-func ReverseTopologicalSort[K comparable, T any](g graph.Graph[K, T], less func(K, K) bool) ([]K, error) {
-	reverseLess := func(a, b K) bool {
-		return !less(b, a)
-	}
-	topo, err := graph.StableTopologicalSort(g, reverseLess)
-	if err != nil {
-		return nil, err
-	}
-	for i := 0; i < len(topo)/2; i++ {
-		topo[i], topo[len(topo)-i-1] = topo[len(topo)-i-1], topo[i]
-	}
-	return topo, nil
-}
diff --git a/pkg/infra/iac3/plugin.go b/pkg/infra/iac3/plugin.go
index b249039a..1ae98799 100644
--- a/pkg/infra/iac3/plugin.go
+++ b/pkg/infra/iac3/plugin.go
@@ -225,15 +225,18 @@ func addPulumiKubernetesProviders(g construct.Graph) error {
 		if !ok {
 			return errors.Join(nerr, fmt.Errorf("resource %s is a kubernetes resource but does not have an id as cluster property (is: %T)", id, cluster))
 		}
-		clusterRes, err := g.Vertex(clusterId)
+		upstreams, err := construct.DirectUpstreamDependencies(g, clusterId)
 		if err != nil {
 			return errors.Join(nerr, err)
 		}
-		kubeconfig, err := clusterRes.GetProperty("KubeConfig")
-		if err != nil {
-			return errors.Join(nerr, err)
+		var kubeconfig construct.ResourceId
+		for _, upstream := range upstreams {
+			if kubeconfigId.Matches(upstream) {
+				kubeconfig = upstream
+				break
+			}
 		}
-		provider, ok := providers[kubeconfig.(construct.ResourceId)]
+		provider, ok := providers[kubeconfig]
 		if !ok {
 			return errors.Join(nerr, fmt.Errorf("resource %s is a kubernetes resource but does not have a provider resource for cluster %s", id, clusterId))
 		}
diff --git a/pkg/knowledge_base2/emitter.go b/pkg/knowledge_base2/emitter.go
index 581ae7a4..1b68b3f5 100644
--- a/pkg/knowledge_base2/emitter.go
+++ b/pkg/knowledge_base2/emitter.go
@@ -90,11 +90,8 @@ func ConsumeFromResource(consumer, emitter *construct.Resource, ctx DynamicConte
 					addErr(consume, emit, err)
 					continue
 				}
-				pval, err := resource.GetProperty(consume.PropertyPath)
-				if err != nil {
-					addErr(consume, emit, err)
-					continue
-				}
+				// we ignore the error here because if we cant get the property we will attempt to apply it as a constraint later on
+				pval, _ := resource.GetProperty(consume.PropertyPath)
 				if consume.Converter != "" {
 					val, err = consume.Convert(val, id, ctx)
 					if err != nil {
diff --git a/pkg/knowledge_base2/kb.go b/pkg/knowledge_base2/kb.go
index 7517756e..34db4448 100644
--- a/pkg/knowledge_base2/kb.go
+++ b/pkg/knowledge_base2/kb.go
@@ -347,7 +347,7 @@ func TransformToPropertyValue(
 }
 
 func TransformAllPropertyValues(ctx DynamicValueContext) error {
-	ids, err := construct.ToplogicalSort(ctx.DAG())
+	ids, err := construct.TopologicalSort(ctx.DAG())
 	if err != nil {
 		return err
 	}
diff --git a/pkg/knowledge_base2/reader/properties.go b/pkg/knowledge_base2/reader/properties.go
index f8e27c06..84741bb5 100644
--- a/pkg/knowledge_base2/reader/properties.go
+++ b/pkg/knowledge_base2/reader/properties.go
@@ -24,6 +24,7 @@ type (
 		Type string `json:"type" yaml:"type"`
 
 		Description string `json:"description" yaml:"description"`
+		IsImportant bool   `json:"important" yaml:"important"`
 
 		Namespace bool `json:"namespace" yaml:"namespace"`
 
diff --git a/pkg/knowledge_base2/resource_template.go b/pkg/knowledge_base2/resource_template.go
index 6670199f..a894ae24 100644
--- a/pkg/knowledge_base2/resource_template.go
+++ b/pkg/knowledge_base2/resource_template.go
@@ -59,10 +59,15 @@ type (
 		DeployTime bool `json:"deploy_time" yaml:"deploy_time"`
 		// OperationalRule defines a rule that is executed at runtime to determine the value of the property
 		OperationalRule *PropertyRule `json:"operational_rule" yaml:"operational_rule"`
-		// Description is a description of the property. This is not used in the engine solving, but is metadata returned by the `ListResourceTypes` CLI command.
+		// Description is a description of the property. This is not used in the engine solving,
+		// but is metadata returned by the `ListResourceTypes` CLI command.
 		Description string `json:"description" yaml:"description"`
 		// Path is the path to the property in the resource
 		Path string `json:"-" yaml:"-"`
+
+		// IsImportant is a flag to denote what properties are subjectively important to show in the InfracopilotUI via
+		// the `ListResourceTypes` CLI command. This field is not & should not be used in the engine.
+		IsImportant bool
 	}
 
 	// Property is an interface used to define a property that exists on a resource
diff --git a/pkg/templates/aws/resources/ecs_task_definition.yaml b/pkg/templates/aws/resources/ecs_task_definition.yaml
index b3316c7e..b6d6853d 100644
--- a/pkg/templates/aws/resources/ecs_task_definition.yaml
+++ b/pkg/templates/aws/resources/ecs_task_definition.yaml
@@ -28,6 +28,7 @@ properties:
   EnvironmentVariables:
     type: map(string,string)
     description: A map of the environment variables to pass to the container
+    important: true
     key_property:
       sanitize: |
         {{ .
diff --git a/pkg/visualizer2/file.go b/pkg/visualizer2/file.go
index 9e30838c..fcc49af3 100644
--- a/pkg/visualizer2/file.go
+++ b/pkg/visualizer2/file.go
@@ -3,6 +3,7 @@ package visualizer
 import (
 	"fmt"
 	"io"
+	"sort"
 	"strings"
 
 	construct "github.com/klothoplatform/klotho/pkg/construct2"
@@ -18,15 +19,8 @@ type (
 		FilenamePrefix string
 		AppName        string
 		Provider       string
-		DAG            construct.Graph
+		Graph          VisGraph
 	}
-
-	VizKey string
-)
-
-const (
-	ParentKey   VizKey = "parent"
-	ChildrenKey VizKey = "children"
 )
 
 func (f *File) Path() string {
@@ -43,70 +37,60 @@ func (f *File) WriteTo(w io.Writer) (n int64, err error) {
 	wh.Writef("provider: %s\n", f.Provider)
 	wh.Write("resources:\n")
 
-	resourceIds, err := construct.ReverseTopologicalSort(f.DAG)
+	resourceIds, err := construct.ReverseTopologicalSort(f.Graph)
+	if err != nil {
+		return
+	}
+	adj, err := f.Graph.AdjacencyMap()
 	if err != nil {
 		return
 	}
-	var resources []*construct.Resource
 	for _, id := range resourceIds {
-		res, err := f.DAG.Vertex(id)
+		res, err := f.Graph.Vertex(id)
 		if err != nil {
 			return n, err
 		}
-		resources = append(resources, res)
-	}
-	for _, resource := range resources {
-		key := f.KeyFor(resource.ID)
-		if key == "" {
+		src := f.KeyFor(id)
+		if src == "" {
 			continue
 		}
-		wh.Writef(indent+"%s:\n", key)
+		wh.Writef(indent+"%s:\n", src)
 
-		// Add any edge properties as metadata
-		_, props, err := f.DAG.VertexWithProperties(resource.ID)
-		if err != nil {
-			return n, err
+		props := make(map[string]any)
+		if res.Tag != "" {
+			props["tag"] = res.Tag
 		}
-		properties := props.Attributes
-
-		for key, val := range properties {
-			if _, ok := properties[key]; ok {
-				if key == string(ChildrenKey) {
-					properties[key] = val
-					continue
-				}
-				id := construct.ResourceId{}
-				err := id.UnmarshalText([]byte(val))
-				if err != nil {
-					properties[key] = val
-					continue
-				}
-				properties[key] = f.KeyFor(id)
+		if !res.Parent.IsZero() {
+			props["parent"] = f.KeyFor(res.Parent)
+		}
+		if len(res.Children) > 0 {
+			childrenIds := res.Children.ToSlice()
+			sort.Sort(construct.SortedIds(childrenIds))
+			children := make([]string, len(childrenIds))
+			for i, child := range childrenIds {
+				children[i] = child.String()
 			}
+			props["children"] = children
 		}
 
-		if len(properties) > 0 {
-			writeYaml(properties, 2, wh)
-		}
-		// Need to write edge properties here tomorrow
-		deps, err := construct.DirectDownstreamDependencies(f.DAG, resource.ID)
-		if err != nil {
-			return n, err
+		if len(props) > 0 {
+			writeYaml(props, 2, wh)
+		} else {
+			wh.Write("\n")
 		}
-		downstreamResources := make([]*construct.Resource, len(deps))
-		for i, dep := range deps {
-			downstreamResources[i], err = f.DAG.Vertex(dep)
-			if err != nil {
-				return n, err
-			}
+
+		deps := adj[id]
+		downstream := make([]construct.ResourceId, 0, len(deps))
+		for dep := range deps {
+			downstream = append(downstream, dep)
 		}
-		for _, res := range downstreamResources {
-			src := f.KeyFor(resource.ID)
-			dst := f.KeyFor(res.ID)
+		sort.Sort(construct.SortedIds(downstream))
+		for _, dep := range downstream {
+			dst := f.KeyFor(dep)
 			if src != "" && dst != "" {
 				wh.Writef(indent+"%s -> %s:\n", src, dst)
 			}
-			dep, err := f.DAG.Edge(resource.ID, res.ID)
+			dep, err := f.Graph.Edge(id, dep)
 			if err != nil {
 				return n, err
 			}
@@ -114,7 +98,6 @@ func (f *File) WriteTo(w io.Writer) (n int64, err error) {
 				writeYaml(dep.Properties.Data, 2, wh)
 			}
 		}
-		wh.Write("\n")
 	}
 
 	return
diff --git a/pkg/visualizer2/plugin.go b/pkg/visualizer2/plugin.go
index 31a9e159..ae7f9e63 100644
--- a/pkg/visualizer2/plugin.go
+++ b/pkg/visualizer2/plugin.go
@@ -68,10 +68,14 @@ func (a *visApi) request(method string, path string, contentType string, accept
 func (p Plugin) Translate(dag construct.Graph) ([]klotho_io.File, error) {
 	api := visApi{client: p.Client}
 
+	var err error
 	spec := &File{
 		AppName:  p.AppName,
 		Provider: p.Provider,
-		DAG:      dag,
+	}
+	spec.Graph, err = ConstructToVis(dag)
+	if err != nil {
+		return nil, err
 	}
 
 	resp, err := api.request(http.MethodPost, `generate-infra-diagram`, "application/yaml", "image/png", spec)
@@ -94,12 +98,17 @@ func (p Plugin) Translate(dag construct.Graph) ([]klotho_io.File, error) {
 func (p Plugin) Generate(dag construct.Graph, filenamePrefix string) ([]klotho_io.File, error) {
 	api := visApi{client: p.Client}
 
+	var err error
 	spec := &File{
 		FilenamePrefix: fmt.Sprintf("%s-", filenamePrefix),
 		AppName:        p.AppName,
 		Provider:       p.Provider,
-		DAG:            dag,
 	}
+	spec.Graph, err = ConstructToVis(dag)
+	if err != nil {
+		return nil, err
+	}
+
 	resp, err := api.request(http.MethodPost, `generate-infra-diagram`, "application/yaml", "image/png", spec)
 	if err != nil {
 		return nil, err
diff --git a/pkg/visualizer2/vis_graph.go b/pkg/visualizer2/vis_graph.go
new file mode 100644
index 00000000..9eb7fca1
--- /dev/null
+++ b/pkg/visualizer2/vis_graph.go
@@ -0,0 +1,53 @@
+package visualizer
+
+import (
+	"errors"
+
+	"github.com/dominikbraun/graph"
+	construct "github.com/klothoplatform/klotho/pkg/construct2"
+	"github.com/klothoplatform/klotho/pkg/graph_addons"
+	"github.com/klothoplatform/klotho/pkg/set"
+)
+
+type (
+	VisResource struct {
+		ID construct.ResourceId
+
+		Tag      string
+		Parent   construct.ResourceId
+		Children set.Set[construct.ResourceId]
+	}
+
+	VisGraph graph.Graph[construct.ResourceId, *VisResource]
+)
+
+func NewVisGraph(options ...func(*graph.Traits)) VisGraph {
+	return VisGraph(graph.NewWithStore(
+		func(r *VisResource) construct.ResourceId { return r.ID },
+		graph_addons.NewMemoryStore[construct.ResourceId, *VisResource](),
+		append(options,
+			graph.Directed(),
+		)...,
+	))
+}
+
+func ConstructToVis(g construct.Graph) (VisGraph, error) {
+	adj, err := g.AdjacencyMap()
+	if err != nil {
+		return nil, err
+	}
+	vis := NewVisGraph()
+	var errs error
+	for id := range adj {
+		errs = errors.Join(errs, vis.AddVertex(&VisResource{ID: id}))
+	}
+	if errs != nil {
+		return nil, errs
+	}
+	for source, targets := range adj {
+		for target := range targets {
+			errs = errors.Join(errs, vis.AddEdge(source, target))
+		}
+	}
+	return vis, errs
+}


Test patch:
diff --git a/pkg/construct2/graphtest/graph.go b/pkg/construct2/graphtest/graph.go
index 5e684206..dad9ba6d 100644
--- a/pkg/construct2/graphtest/graph.go
+++ b/pkg/construct2/graphtest/graph.go
@@ -44,7 +44,7 @@ func AssertGraphContains(t *testing.T, expect, actual construct2.Graph) {
 		return v
 	}
 
-	expectVs := must(construct2.ToplogicalSort(expect)).([]construct2.ResourceId)
+	expectVs := must(construct2.TopologicalSort(expect)).([]construct2.ResourceId)
 	for _, expectV := range expectVs {
 		_, err := actual.Vertex(expectV)
 		assert.NoError(err)
diff --git a/pkg/construct2/graphtest/graph_vertices_test.go b/pkg/construct2/graphtest/graph_vertices_test.go
index b7c1d519..e2f7a3fb 100644
--- a/pkg/construct2/graphtest/graph_vertices_test.go
+++ b/pkg/construct2/graphtest/graph_vertices_test.go
@@ -80,7 +80,7 @@ func TestToplogicalSort(t *testing.T) {
 
 			// Repeat to make sure it's fully deterministic
 			for i := 0; i < 100; i++ {
-				got, err := construct.ToplogicalSort(tt.graph)
+				got, err := construct.TopologicalSort(tt.graph)
 				require.NoError(err)
 				gotStr := make([]string, len(got))
 				for i, v := range got {
diff --git a/pkg/engine2/testdata/2_routes.dataflow-viz.yaml b/pkg/engine2/testdata/2_routes.dataflow-viz.yaml
index 9c3d954e..7130dda8 100755
--- a/pkg/engine2/testdata/2_routes.dataflow-viz.yaml
+++ b/pkg/engine2/testdata/2_routes.dataflow-viz.yaml
@@ -1,26 +1,58 @@
 provider: aws
 resources:
   lambda_function/lambda_function_1:
-    children: aws:ecr_image:lambda_function_1-image,aws:iam_role:lambda_function_1-ExecutionRole,aws:log_group:lambda_function_1-log-group
-
+    children:
+        - aws:ecr_image:lambda_function_1-image
+        - aws:iam_role:lambda_function_1-ExecutionRole
+    parent: rest_api/rest_api_1
+    tag: big
 
   lambda_function/lambda_function_0:
-    children: aws:ecr_image:lambda_function_0-image,aws:iam_role:lambda_function_0-ExecutionRole,aws:log_group:lambda_function_0-log-group
-
+    children:
+        - aws:ecr_image:lambda_function_0-image
+        - aws:ecr_repo:ecr_repo-0
+        - aws:iam_role:lambda_function_0-ExecutionRole
+        - aws:log_group:lambda_function_0-log-group
+        - aws:log_group:lambda_function_1-log-group
+    parent: rest_api/rest_api_1
+    tag: big
 
   rest_api/rest_api_1:
+    children:
+        - aws:api_resource:rest_api_1:lambda0
+        - aws:api_resource:rest_api_1:lambda1
+        - aws:api_stage:rest_api_1:api_stage-0
+    tag: parent
 
   aws:api_integration:rest_api_1/integ1:
+    children:
+        - aws:api_deployment:rest_api_1:api_deployment-0
+        - aws:api_method:rest_api_1:integ0-api_method
+        - aws:api_method:rest_api_1:integ1-api_method
+        - aws:api_resource:rest_api_1:api_resource-0
+        - aws:api_resource:rest_api_1:api_resource-1
+        - aws:api_resource:rest_api_1:lambda0
+        - aws:api_resource:rest_api_1:lambda1
+        - aws:api_stage:rest_api_1:api_stage-0
+        - aws:lambda_permission:integ0-lambda_function_0
+        - aws:lambda_permission:integ1-lambda_function_1
     parent: rest_api/rest_api_1
+    tag: big
 
   aws:api_integration:rest_api_1/integ1 -> lambda_function/lambda_function_1:
-    path: aws:lambda_permission:integ1-lambda_function_1
-
-
   aws:api_integration:rest_api_1/integ0:
+    children:
+        - aws:api_deployment:rest_api_1:api_deployment-0
+        - aws:api_method:rest_api_1:integ0-api_method
+        - aws:api_method:rest_api_1:integ1-api_method
+        - aws:api_resource:rest_api_1:api_resource-0
+        - aws:api_resource:rest_api_1:api_resource-1
+        - aws:api_resource:rest_api_1:lambda0
+        - aws:api_resource:rest_api_1:lambda1
+        - aws:api_stage:rest_api_1:api_stage-0
+        - aws:lambda_permission:integ0-lambda_function_0
+        - aws:lambda_permission:integ1-lambda_function_1
     parent: rest_api/rest_api_1
+    tag: big
 
   aws:api_integration:rest_api_1/integ0 -> lambda_function/lambda_function_0:
-    path: aws:lambda_permission:integ0-lambda_function_0
-
-
diff --git a/pkg/engine2/testdata/2_routes.iac-viz.yaml b/pkg/engine2/testdata/2_routes.iac-viz.yaml
index 371ab128..8a387a88 100755
--- a/pkg/engine2/testdata/2_routes.iac-viz.yaml
+++ b/pkg/engine2/testdata/2_routes.iac-viz.yaml
@@ -9,77 +9,77 @@ resources:
   log_group/lambda_function_1-log-group:
 
   aws:api_resource:rest_api_1/lambda0:
-  aws:api_resource:rest_api_1/lambda0 -> rest_api/rest_api_1:
 
+  aws:api_resource:rest_api_1/lambda0 -> rest_api/rest_api_1:
   ecr_image/lambda_function_0-image:
-  ecr_image/lambda_function_0-image -> ecr_repo/ecr_repo-0:
 
+  ecr_image/lambda_function_0-image -> ecr_repo/ecr_repo-0:
   iam_role/lambda_function_0-executionrole:
-  iam_role/lambda_function_0-executionrole -> log_group/lambda_function_0-log-group:
 
+  iam_role/lambda_function_0-executionrole -> log_group/lambda_function_0-log-group:
   aws:api_resource:rest_api_1/lambda1:
-  aws:api_resource:rest_api_1/lambda1 -> rest_api/rest_api_1:
 
+  aws:api_resource:rest_api_1/lambda1 -> rest_api/rest_api_1:
   ecr_image/lambda_function_1-image:
-  ecr_image/lambda_function_1-image -> ecr_repo/ecr_repo-0:
 
+  ecr_image/lambda_function_1-image -> ecr_repo/ecr_repo-0:
   iam_role/lambda_function_1-executionrole:
-  iam_role/lambda_function_1-executionrole -> log_group/lambda_function_1-log-group:
 
+  iam_role/lambda_function_1-executionrole -> log_group/lambda_function_1-log-group:
   aws:api_resource:rest_api_1/api_resource-0:
+
   aws:api_resource:rest_api_1/api_resource-0 -> aws:api_resource:rest_api_1/lambda0:
   aws:api_resource:rest_api_1/api_resource-0 -> rest_api/rest_api_1:
-
   lambda_function/lambda_function_0:
+
   lambda_function/lambda_function_0 -> ecr_image/lambda_function_0-image:
   lambda_function/lambda_function_0 -> iam_role/lambda_function_0-executionrole:
-
   aws:api_resource:rest_api_1/api_resource-1:
+
   aws:api_resource:rest_api_1/api_resource-1 -> aws:api_resource:rest_api_1/lambda1:
   aws:api_resource:rest_api_1/api_resource-1 -> rest_api/rest_api_1:
-
   lambda_function/lambda_function_1:
+
   lambda_function/lambda_function_1 -> ecr_image/lambda_function_1-image:
   lambda_function/lambda_function_1 -> iam_role/lambda_function_1-executionrole:
-
   aws:api_method:rest_api_1/integ0-api_method:
+
   aws:api_method:rest_api_1/integ0-api_method -> aws:api_resource:rest_api_1/api_resource-0:
   aws:api_method:rest_api_1/integ0-api_method -> rest_api/rest_api_1:
-
   lambda_permission/integ0-lambda_function_0:
+
   lambda_permission/integ0-lambda_function_0 -> lambda_function/lambda_function_0:
   lambda_permission/integ0-lambda_function_0 -> rest_api/rest_api_1:
-
   aws:api_method:rest_api_1/integ1-api_method:
+
   aws:api_method:rest_api_1/integ1-api_method -> aws:api_resource:rest_api_1/api_resource-1:
   aws:api_method:rest_api_1/integ1-api_method -> rest_api/rest_api_1:
-
   lambda_permission/integ1-lambda_function_1:
+
   lambda_permission/integ1-lambda_function_1 -> lambda_function/lambda_function_1:
   lambda_permission/integ1-lambda_function_1 -> rest_api/rest_api_1:
-
   aws:api_integration:rest_api_1/integ0:
+
   aws:api_integration:rest_api_1/integ0 -> aws:api_method:rest_api_1/integ0-api_method:
   aws:api_integration:rest_api_1/integ0 -> aws:api_resource:rest_api_1/api_resource-0:
   aws:api_integration:rest_api_1/integ0 -> lambda_function/lambda_function_0:
   aws:api_integration:rest_api_1/integ0 -> lambda_permission/integ0-lambda_function_0:
   aws:api_integration:rest_api_1/integ0 -> rest_api/rest_api_1:
-
   aws:api_integration:rest_api_1/integ1:
+
   aws:api_integration:rest_api_1/integ1 -> aws:api_method:rest_api_1/integ1-api_method:
   aws:api_integration:rest_api_1/integ1 -> aws:api_resource:rest_api_1/api_resource-1:
   aws:api_integration:rest_api_1/integ1 -> lambda_function/lambda_function_1:
   aws:api_integration:rest_api_1/integ1 -> lambda_permission/integ1-lambda_function_1:
   aws:api_integration:rest_api_1/integ1 -> rest_api/rest_api_1:
-
   aws:api_deployment:rest_api_1/api_deployment-0:
+
   aws:api_deployment:rest_api_1/api_deployment-0 -> aws:api_integration:rest_api_1/integ0:
   aws:api_deployment:rest_api_1/api_deployment-0 -> aws:api_integration:rest_api_1/integ1:
   aws:api_deployment:rest_api_1/api_deployment-0 -> aws:api_method:rest_api_1/integ0-api_method:
   aws:api_deployment:rest_api_1/api_deployment-0 -> aws:api_method:rest_api_1/integ1-api_method:
   aws:api_deployment:rest_api_1/api_deployment-0 -> rest_api/rest_api_1:
-
   aws:api_stage:rest_api_1/api_stage-0:
+
   aws:api_stage:rest_api_1/api_stage-0 -> aws:api_deployment:rest_api_1/api_deployment-0:
   aws:api_stage:rest_api_1/api_stage-0 -> rest_api/rest_api_1:
-
diff --git a/pkg/engine2/testdata/delete_namespace_resource.dataflow-viz.yaml b/pkg/engine2/testdata/delete_namespace_resource.dataflow-viz.yaml
index d1ee6387..6989b166 100644
--- a/pkg/engine2/testdata/delete_namespace_resource.dataflow-viz.yaml
+++ b/pkg/engine2/testdata/delete_namespace_resource.dataflow-viz.yaml
@@ -1,6 +1,10 @@
 provider: aws
 resources:
   lambda_function/lambda_function_2:
-    children: aws:ecr_image:lambda_function_2-image,aws:iam_role:lambda_function_2-ExecutionRole,aws:log_group:lambda_function_2-log-group
-
+    children:
+        - aws:ecr_image:lambda_function_2-image
+        - aws:ecr_repo:ecr_repo-0
+        - aws:iam_role:lambda_function_2-ExecutionRole
+        - aws:log_group:lambda_function_2-log-group
+    tag: big
 
diff --git a/pkg/engine2/testdata/delete_namespace_resource.iac-viz.yaml b/pkg/engine2/testdata/delete_namespace_resource.iac-viz.yaml
index 3ce4c0d2..44854cd8 100644
--- a/pkg/engine2/testdata/delete_namespace_resource.iac-viz.yaml
+++ b/pkg/engine2/testdata/delete_namespace_resource.iac-viz.yaml
@@ -5,13 +5,13 @@ resources:
   log_group/lambda_function_2-log-group:
 
   ecr_image/lambda_function_2-image:
-  ecr_image/lambda_function_2-image -> ecr_repo/ecr_repo-0:
 
+  ecr_image/lambda_function_2-image -> ecr_repo/ecr_repo-0:
   iam_role/lambda_function_2-executionrole:
-  iam_role/lambda_function_2-executionrole -> log_group/lambda_function_2-log-group:
 
+  iam_role/lambda_function_2-executionrole -> log_group/lambda_function_2-log-group:
   lambda_function/lambda_function_2:
+
   lambda_function/lambda_function_2 -> ecr_image/lambda_function_2-image:
   lambda_function/lambda_function_2 -> iam_role/lambda_function_2-executionrole:
   lambda_function/lambda_function_2 -> log_group/lambda_function_2-log-group:
-
diff --git a/pkg/engine2/testdata/delete_resource_and_iacdeps.dataflow-viz.yaml b/pkg/engine2/testdata/delete_resource_and_iacdeps.dataflow-viz.yaml
index 694483be..c1861776 100644
--- a/pkg/engine2/testdata/delete_resource_and_iacdeps.dataflow-viz.yaml
+++ b/pkg/engine2/testdata/delete_resource_and_iacdeps.dataflow-viz.yaml
@@ -1,8 +1,16 @@
 provider: aws
 resources:
   rest_api/rest_api_0:
+    children:
+        - aws:api_stage:rest_api_0:api_stage-0
+    tag: parent
 
   aws:api_integration:rest_api_0/rest_api_0_integration_0:
+    children:
+        - aws:api_deployment:rest_api_0:api_deployment-0
+        - aws:api_method:rest_api_0:rest_api_0_integration_0_method
+        - aws:api_resource:rest_api_0:api_resource-0
+        - aws:api_stage:rest_api_0:api_stage-0
     parent: rest_api/rest_api_0
-
+    tag: big
 
diff --git a/pkg/engine2/testdata/delete_resource_and_iacdeps.iac-viz.yaml b/pkg/engine2/testdata/delete_resource_and_iacdeps.iac-viz.yaml
index 23606bb1..0de6a13d 100644
--- a/pkg/engine2/testdata/delete_resource_and_iacdeps.iac-viz.yaml
+++ b/pkg/engine2/testdata/delete_resource_and_iacdeps.iac-viz.yaml
@@ -3,25 +3,25 @@ resources:
   rest_api/rest_api_0:
 
   aws:api_resource:rest_api_0/api_resource-0:
-  aws:api_resource:rest_api_0/api_resource-0 -> rest_api/rest_api_0:
 
+  aws:api_resource:rest_api_0/api_resource-0 -> rest_api/rest_api_0:
   aws:api_method:rest_api_0/rest_api_0_integration_0_method:
+
   aws:api_method:rest_api_0/rest_api_0_integration_0_method -> aws:api_resource:rest_api_0/api_resource-0:
   aws:api_method:rest_api_0/rest_api_0_integration_0_method -> rest_api/rest_api_0:
-
   aws:api_integration:rest_api_0/rest_api_0_integration_0:
+
   aws:api_integration:rest_api_0/rest_api_0_integration_0 -> aws:api_method:rest_api_0/rest_api_0_integration_0_method:
   aws:api_integration:rest_api_0/rest_api_0_integration_0 -> aws:api_resource:rest_api_0/api_resource-0:
   aws:api_integration:rest_api_0/rest_api_0_integration_0 -> rest_api/rest_api_0:
-
   aws:api_deployment:rest_api_0/api_deployment-0:
+
   aws:api_deployment:rest_api_0/api_deployment-0 -> aws:api_integration:rest_api_0/rest_api_0_integration_0:
   aws:api_deployment:rest_api_0/api_deployment-0 -> aws:api_method:rest_api_0/rest_api_0_integration_0_method:
   aws:api_deployment:rest_api_0/api_deployment-0 -> rest_api/rest_api_0:
-
   ecs_cluster/ecs_cluster-0:
 
   aws:api_stage:rest_api_0/api_stage-0:
+
   aws:api_stage:rest_api_0/api_stage-0 -> aws:api_deployment:rest_api_0/api_deployment-0:
   aws:api_stage:rest_api_0/api_stage-0 -> rest_api/rest_api_0:
-
diff --git a/pkg/engine2/testdata/ecs_rds.dataflow-viz.yaml b/pkg/engine2/testdata/ecs_rds.dataflow-viz.yaml
index 63130dfb..405aa42e 100755
--- a/pkg/engine2/testdata/ecs_rds.dataflow-viz.yaml
+++ b/pkg/engine2/testdata/ecs_rds.dataflow-viz.yaml
@@ -1,17 +1,42 @@
 provider: aws
 resources:
   rds_instance/rds-instance-2:
-    children: aws:rds_subnet_group:rds_subnet_group-0,aws:security_group:vpc-0:rds-instance-2-security_group
+    children:
+        - aws:iam_role:ecs_service_0-execution-role
+        - aws:rds_subnet_group:rds_subnet_group-0
+        - aws:security_group:vpc-0:rds-instance-2-security_group
     parent: vpc/vpc-0
-
+    tag: big
 
   vpc/vpc-0:
+    children:
+        - aws:ecr_image:ecs_service_0-image
+        - aws:ecr_repo:ecr_repo-0
+        - aws:ecs_task_definition:ecs_service_0
+        - aws:elastic_ip:subnet-0-route_table-nat_gateway-elastic_ip
+        - aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip
+        - aws:iam_role:ecs_service_0-execution-role
+        - aws:internet_gateway:vpc-0:internet_gateway-0
+        - aws:log_group:ecs_service_0-log-group
+        - aws:nat_gateway:subnet-2:subnet-0-route_table-nat_gateway
+        - aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway
+        - aws:rds_subnet_group:rds_subnet_group-0
+        - aws:route_table:vpc-0:subnet-0-route_table
+        - aws:route_table:vpc-0:subnet-1-route_table
+        - aws:route_table:vpc-0:subnet-2-route_table
+        - aws:route_table:vpc-0:subnet-3-route_table
+        - aws:security_group:vpc-0:ecs_service_0-security_group
+        - aws:security_group:vpc-0:rds-instance-2-security_group
+    tag: parent
 
   ecs_service/ecs_service_0:
-    children: aws:ecs_task_definition:ecs_service_0
+    children:
+        - aws:ecr_image:ecs_service_0-image
+        - aws:ecr_repo:ecr_repo-0
+        - aws:ecs_task_definition:ecs_service_0
+        - aws:log_group:ecs_service_0-log-group
+        - aws:security_group:vpc-0:ecs_service_0-security_group
     parent: vpc/vpc-0
+    tag: big
 
   ecs_service/ecs_service_0 -> rds_instance/rds-instance-2:
-    path: aws:ecs_task_definition:ecs_service_0,aws:iam_role:ecs_service_0-execution-role
-
-
diff --git a/pkg/engine2/testdata/ecs_rds.expect.yaml b/pkg/engine2/testdata/ecs_rds.expect.yaml
index 7a20d6dc..674b9038 100755
--- a/pkg/engine2/testdata/ecs_rds.expect.yaml
+++ b/pkg/engine2/testdata/ecs_rds.expect.yaml
@@ -169,11 +169,6 @@ resources:
               Protocol: "-1"
               ToPort: 0
         IngressRules:
-            - Description: Allow ingress traffic from within the same security group
-              FromPort: 0
-              Protocol: "-1"
-              Self: true
-              ToPort: 0
             - CidrBlocks:
                 - 10.0.128.0/18
               Description: Allow ingress traffic from ip addresses within the subnet subnet-0
@@ -186,6 +181,11 @@ resources:
               FromPort: 0
               Protocol: "-1"
               ToPort: 0
+            - Description: Allow ingress traffic from within the same security group
+              FromPort: 0
+              Protocol: "-1"
+              Self: true
+              ToPort: 0
         Vpc: aws:vpc:vpc-0
     aws:route_table:vpc-0:subnet-0-route_table:
         Routes:
diff --git a/pkg/engine2/testdata/ecs_rds.iac-viz.yaml b/pkg/engine2/testdata/ecs_rds.iac-viz.yaml
index 529e3e67..1f9e8b1c 100755
--- a/pkg/engine2/testdata/ecs_rds.iac-viz.yaml
+++ b/pkg/engine2/testdata/ecs_rds.iac-viz.yaml
@@ -5,113 +5,113 @@ resources:
   vpc/vpc-0:
 
   aws:availability_zone:region-0/availability_zone-0:
-  aws:availability_zone:region-0/availability_zone-0 -> region/region-0:
 
+  aws:availability_zone:region-0/availability_zone-0 -> region/region-0:
   aws:security_group:vpc-0/rds-instance-2-security_group:
-  aws:security_group:vpc-0/rds-instance-2-security_group -> vpc/vpc-0:
 
+  aws:security_group:vpc-0/rds-instance-2-security_group -> vpc/vpc-0:
   aws:availability_zone:region-0/availability_zone-1:
-  aws:availability_zone:region-0/availability_zone-1 -> region/region-0:
 
+  aws:availability_zone:region-0/availability_zone-1 -> region/region-0:
   aws:subnet:vpc-0/subnet-0:
+
   aws:subnet:vpc-0/subnet-0 -> aws:availability_zone:region-0/availability_zone-0:
   aws:subnet:vpc-0/subnet-0 -> aws:security_group:vpc-0/rds-instance-2-security_group:
   aws:subnet:vpc-0/subnet-0 -> vpc/vpc-0:
-
   aws:subnet:vpc-0/subnet-1:
+
   aws:subnet:vpc-0/subnet-1 -> aws:availability_zone:region-0/availability_zone-1:
   aws:subnet:vpc-0/subnet-1 -> aws:security_group:vpc-0/rds-instance-2-security_group:
   aws:subnet:vpc-0/subnet-1 -> vpc/vpc-0:
-
   rds_subnet_group/rds_subnet_group-0:
+
   rds_subnet_group/rds_subnet_group-0 -> aws:subnet:vpc-0/subnet-0:
   rds_subnet_group/rds_subnet_group-0 -> aws:subnet:vpc-0/subnet-1:
-
   elastic_ip/subnet-1-route_table-nat_gateway-elastic_ip:
 
   aws:subnet:vpc-0/subnet-3:
+
   aws:subnet:vpc-0/subnet-3 -> aws:availability_zone:region-0/availability_zone-1:
   aws:subnet:vpc-0/subnet-3 -> vpc/vpc-0:
-
   elastic_ip/subnet-0-route_table-nat_gateway-elastic_ip:
 
   aws:subnet:vpc-0/subnet-2:
+
   aws:subnet:vpc-0/subnet-2 -> aws:availability_zone:region-0/availability_zone-0:
   aws:subnet:vpc-0/subnet-2 -> vpc/vpc-0:
-
   ecr_repo/ecr_repo-0:
 
   rds_instance/rds-instance-2:
+
   rds_instance/rds-instance-2 -> rds_subnet_group/rds_subnet_group-0:
   rds_instance/rds-instance-2 -> aws:security_group:vpc-0/rds-instance-2-security_group:
-
   aws:internet_gateway:vpc-0/internet_gateway-0:
-  aws:internet_gateway:vpc-0/internet_gateway-0 -> vpc/vpc-0:
 
+  aws:internet_gateway:vpc-0/internet_gateway-0 -> vpc/vpc-0:
   aws:nat_gateway:subnet-3/subnet-1-route_table-nat_gateway:
+
   aws:nat_gateway:subnet-3/subnet-1-route_table-nat_gateway -> elastic_ip/subnet-1-route_table-nat_gateway-elastic_ip:
   aws:nat_gateway:subnet-3/subnet-1-route_table-nat_gateway -> aws:subnet:vpc-0/subnet-3:
-
   aws:nat_gateway:subnet-2/subnet-0-route_table-nat_gateway:
+
   aws:nat_gateway:subnet-2/subnet-0-route_table-nat_gateway -> elastic_ip/subnet-0-route_table-nat_gateway-elastic_ip:
   aws:nat_gateway:subnet-2/subnet-0-route_table-nat_gateway -> aws:subnet:vpc-0/subnet-2:
-
   ecr_image/ecs_service_0-image:
-  ecr_image/ecs_service_0-image -> ecr_repo/ecr_repo-0:
 
+  ecr_image/ecs_service_0-image -> ecr_repo/ecr_repo-0:
   iam_role/ecs_service_0-execution-role:
-  iam_role/ecs_service_0-execution-role -> rds_instance/rds-instance-2:
 
+  iam_role/ecs_service_0-execution-role -> rds_instance/rds-instance-2:
   log_group/ecs_service_0-log-group:
 
   aws:route_table:vpc-0/subnet-3-route_table:
+
   aws:route_table:vpc-0/subnet-3-route_table -> aws:internet_gateway:vpc-0/internet_gateway-0:
   aws:route_table:vpc-0/subnet-3-route_table -> vpc/vpc-0:
-
   aws:route_table:vpc-0/subnet-2-route_table:
+
   aws:route_table:vpc-0/subnet-2-route_table -> aws:internet_gateway:vpc-0/internet_gateway-0:
   aws:route_table:vpc-0/subnet-2-route_table -> vpc/vpc-0:
-
   aws:route_table:vpc-0/subnet-1-route_table:
+
   aws:route_table:vpc-0/subnet-1-route_table -> aws:nat_gateway:subnet-3/subnet-1-route_table-nat_gateway:
   aws:route_table:vpc-0/subnet-1-route_table -> vpc/vpc-0:
-
   aws:route_table:vpc-0/subnet-0-route_table:
+
   aws:route_table:vpc-0/subnet-0-route_table -> aws:nat_gateway:subnet-2/subnet-0-route_table-nat_gateway:
   aws:route_table:vpc-0/subnet-0-route_table -> vpc/vpc-0:
-
   ecs_cluster/ecs_cluster-0:
 
   ecs_task_definition/ecs_service_0:
+
   ecs_task_definition/ecs_service_0 -> ecr_image/ecs_service_0-image:
   ecs_task_definition/ecs_service_0 -> iam_role/ecs_service_0-execution-role:
   ecs_task_definition/ecs_service_0 -> log_group/ecs_service_0-log-group:
   ecs_task_definition/ecs_service_0 -> rds_instance/rds-instance-2:
   ecs_task_definition/ecs_service_0 -> region/region-0:
-
   aws:security_group:vpc-0/ecs_service_0-security_group:
-  aws:security_group:vpc-0/ecs_service_0-security_group -> vpc/vpc-0:
 
+  aws:security_group:vpc-0/ecs_service_0-security_group -> vpc/vpc-0:
   route_table_association/subnet-3-subnet-3-route_table:
+
   route_table_association/subnet-3-subnet-3-route_table -> aws:route_table:vpc-0/subnet-3-route_table:
   route_table_association/subnet-3-subnet-3-route_table -> aws:subnet:vpc-0/subnet-3:
-
   route_table_association/subnet-2-subnet-2-route_table:
+
   route_table_association/subnet-2-subnet-2-route_table -> aws:route_table:vpc-0/subnet-2-route_table:
   route_table_association/subnet-2-subnet-2-route_table -> aws:subnet:vpc-0/subnet-2:
-
   route_table_association/subnet-1-subnet-1-route_table:
+
   route_table_association/subnet-1-subnet-1-route_table -> aws:route_table:vpc-0/subnet-1-route_table:
   route_table_association/subnet-1-subnet-1-route_table -> aws:subnet:vpc-0/subnet-1:
-
   route_table_association/subnet-0-subnet-0-route_table:
+
   route_table_association/subnet-0-subnet-0-route_table -> aws:route_table:vpc-0/subnet-0-route_table:
   route_table_association/subnet-0-subnet-0-route_table -> aws:subnet:vpc-0/subnet-0:
-
   ecs_service/ecs_service_0:
+
   ecs_service/ecs_service_0 -> ecs_cluster/ecs_cluster-0:
   ecs_service/ecs_service_0 -> ecs_task_definition/ecs_service_0:
   ecs_service/ecs_service_0 -> aws:security_group:vpc-0/ecs_service_0-security_group:
   ecs_service/ecs_service_0 -> aws:subnet:vpc-0/subnet-0:
   ecs_service/ecs_service_0 -> aws:subnet:vpc-0/subnet-1:
-
diff --git a/pkg/engine2/testdata/extend_graph.dataflow-viz.yaml b/pkg/engine2/testdata/extend_graph.dataflow-viz.yaml
index 3fe44517..2042458e 100755
--- a/pkg/engine2/testdata/extend_graph.dataflow-viz.yaml
+++ b/pkg/engine2/testdata/extend_graph.dataflow-viz.yaml
@@ -1,17 +1,30 @@
 provider: aws
 resources:
   lambda_function/lambda_function_0:
-    children: aws:ecr_image:lambda_function_0-image,aws:iam_role:lambda_function_0-ExecutionRole,aws:log_group:lambda_function_0-log-group
-
+    children:
+        - aws:ecr_image:lambda_function_0-image
+        - aws:ecr_repo:ecr_repo-0
+        - aws:iam_role:lambda_function_0-ExecutionRole
+        - aws:log_group:lambda_function_0-log-group
+    parent: rest_api/rest_api_1
+    tag: big
 
   rest_api/rest_api_1:
+    children:
+        - aws:api_stage:rest_api_1:api_stage-0
+    tag: parent
 
   dynamodb_table/dynamodb_table_3:
+    tag: big
 
   aws:api_integration:rest_api_1/rest_api_1_integration_0:
+    children:
+        - aws:api_deployment:rest_api_1:api_deployment-0
+        - aws:api_method:rest_api_1:rest_api_1_integration_0_method
+        - aws:api_resource:rest_api_1:api_resource-0
+        - aws:api_stage:rest_api_1:api_stage-0
+        - aws:lambda_permission:rest_api_1_integration_0_lambda_function_0
     parent: rest_api/rest_api_1
+    tag: big
 
   aws:api_integration:rest_api_1/rest_api_1_integration_0 -> lambda_function/lambda_function_0:
-    path: aws:lambda_permission:rest_api_1_integration_0_lambda_function_0
-
-
diff --git a/pkg/engine2/testdata/extend_graph.iac-viz.yaml b/pkg/engine2/testdata/extend_graph.iac-viz.yaml
index 827bcc82..ee0249a9 100755
--- a/pkg/engine2/testdata/extend_graph.iac-viz.yaml
+++ b/pkg/engine2/testdata/extend_graph.iac-viz.yaml
@@ -7,42 +7,42 @@ resources:
   rest_api/rest_api_1:
 
   ecr_image/lambda_function_0-image:
-  ecr_image/lambda_function_0-image -> ecr_repo/ecr_repo-0:
 
+  ecr_image/lambda_function_0-image -> ecr_repo/ecr_repo-0:
   iam_role/lambda_function_0-executionrole:
-  iam_role/lambda_function_0-executionrole -> log_group/lambda_function_0-log-group:
 
+  iam_role/lambda_function_0-executionrole -> log_group/lambda_function_0-log-group:
   aws:api_resource:rest_api_1/api_resource-0:
-  aws:api_resource:rest_api_1/api_resource-0 -> rest_api/rest_api_1:
 
+  aws:api_resource:rest_api_1/api_resource-0 -> rest_api/rest_api_1:
   lambda_function/lambda_function_0:
+
   lambda_function/lambda_function_0 -> ecr_image/lambda_function_0-image:
   lambda_function/lambda_function_0 -> iam_role/lambda_function_0-executionrole:
   lambda_function/lambda_function_0 -> log_group/lambda_function_0-log-group:
-
   aws:api_method:rest_api_1/rest_api_1_integration_0_method:
+
   aws:api_method:rest_api_1/rest_api_1_integration_0_method -> aws:api_resource:rest_api_1/api_resource-0:
   aws:api_method:rest_api_1/rest_api_1_integration_0_method -> rest_api/rest_api_1:
-
   lambda_permission/rest_api_1_integration_0_lambda_function_0:
+
   lambda_permission/rest_api_1_integration_0_lambda_function_0 -> lambda_function/lambda_function_0:
   lambda_permission/rest_api_1_integration_0_lambda_function_0 -> rest_api/rest_api_1:
-
   aws:api_integration:rest_api_1/rest_api_1_integration_0:
+
   aws:api_integration:rest_api_1/rest_api_1_integration_0 -> aws:api_method:rest_api_1/rest_api_1_integration_0_method:
   aws:api_integration:rest_api_1/rest_api_1_integration_0 -> aws:api_resource:rest_api_1/api_resource-0:
   aws:api_integration:rest_api_1/rest_api_1_integration_0 -> lambda_function/lambda_function_0:
   aws:api_integration:rest_api_1/rest_api_1_integration_0 -> lambda_permission/rest_api_1_integration_0_lambda_function_0:
   aws:api_integration:rest_api_1/rest_api_1_integration_0 -> rest_api/rest_api_1:
-
   aws:api_deployment:rest_api_1/api_deployment-0:
+
   aws:api_deployment:rest_api_1/api_deployment-0 -> aws:api_integration:rest_api_1/rest_api_1_integration_0:
   aws:api_deployment:rest_api_1/api_deployment-0 -> aws:api_method:rest_api_1/rest_api_1_integration_0_method:
   aws:api_deployment:rest_api_1/api_deployment-0 -> rest_api/rest_api_1:
-
   dynamodb_table/dynamodb_table_3:
 
   aws:api_stage:rest_api_1/api_stage-0:
+
   aws:api_stage:rest_api_1/api_stage-0 -> aws:api_deployment:rest_api_1/api_deployment-0:
   aws:api_stage:rest_api_1/api_stage-0 -> rest_api/rest_api_1:
-
diff --git a/pkg/engine2/testdata/k8s_api.dataflow-viz.yaml b/pkg/engine2/testdata/k8s_api.dataflow-viz.yaml
index 51f217bc..1b68ae8b 100755
--- a/pkg/engine2/testdata/k8s_api.dataflow-viz.yaml
+++ b/pkg/engine2/testdata/k8s_api.dataflow-viz.yaml
@@ -1,37 +1,196 @@
 provider: aws
 resources:
   kubernetes:pod:eks_cluster-0/pod2:
+    children:
+        - aws:ecr_image:pod2-ecr_image
+        - aws:ecr_repo:ecr_repo-0
     parent: eks_cluster/eks_cluster-0
-
+    tag: big
 
   load_balancer/rest-api-4-integbcc77100:
-    parent: vpc/vpc-0
+    children:
+        - aws:api_deployment:rest_api_4:api_deployment-0
+        - aws:api_method:rest_api_4:rest_api_4_integration_0_method
+        - aws:api_resource:rest_api_4:api_resource-0
+        - aws:api_stage:rest_api_4:api_stage-0
+        - aws:eks_node_group:eks_node_group-0
+        - aws:elastic_ip:subnet-0-route_table-nat_gateway-elastic_ip
+        - aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip
+        - aws:iam_oidc_provider:eks_cluster-0
+        - aws:iam_policy:iam_policy-0
+        - aws:iam_role:aws-load-balancer-controller
+        - aws:iam_role:eks_node_group-0-iam_role
+        - aws:iam_role:pod2
+        - aws:internet_gateway:vpc-0:internet_gateway-0
+        - aws:load_balancer_listener:rest_api_4_integration_0-pod2
+        - aws:nat_gateway:subnet-2:subnet-0-route_table-nat_gateway
+        - aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway
+        - aws:route_table:vpc-0:subnet-0-route_table
+        - aws:route_table:vpc-0:subnet-1-route_table
+        - aws:route_table:vpc-0:subnet-2-route_table
+        - aws:route_table:vpc-0:subnet-3-route_table
+        - aws:security_group:vpc-0:eks_cluster-0-security_group
+        - aws:target_group:rest-api-4-integbcc77100
+        - aws:vpc_link:rest_api_4_integration_0-pod2
+        - kubernetes:service:eks_cluster-0:restapi4integration0-pod2
+        - kubernetes:service_account:eks_cluster-0:aws-load-balancer-controller
+        - kubernetes:service_account:eks_cluster-0:pod2
+        - kubernetes:target_group_binding:eks_cluster-0:restapi4integration0-pod2
+    parent: eks_cluster/eks_cluster-0
+    tag: big
 
   load_balancer/rest-api-4-integbcc77100 -> kubernetes:pod:eks_cluster-0/pod2:
-    path: aws:load_balancer_listener:rest_api_4_integration_0-pod2,aws:target_group:rest-api-4-integbcc77100,kubernetes:target_group_binding:eks_cluster-0:restapi4integration0-pod2,kubernetes:service:eks_cluster-0:restapi4integration0-pod2
-
-
   kubernetes:helm_chart:eks_cluster-0/metricsserver:
+    children:
+        - aws:api_deployment:rest_api_4:api_deployment-0
+        - aws:api_method:rest_api_4:rest_api_4_integration_0_method
+        - aws:api_resource:rest_api_4:api_resource-0
+        - aws:api_stage:rest_api_4:api_stage-0
+        - aws:eks_node_group:eks_node_group-0
+        - aws:elastic_ip:subnet-0-route_table-nat_gateway-elastic_ip
+        - aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip
+        - aws:iam_oidc_provider:eks_cluster-0
+        - aws:iam_policy:iam_policy-0
+        - aws:iam_role:aws-load-balancer-controller
+        - aws:iam_role:eks_node_group-0-iam_role
+        - aws:iam_role:pod2
+        - aws:internet_gateway:vpc-0:internet_gateway-0
+        - aws:load_balancer_listener:rest_api_4_integration_0-pod2
+        - aws:nat_gateway:subnet-2:subnet-0-route_table-nat_gateway
+        - aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway
+        - aws:route_table:vpc-0:subnet-0-route_table
+        - aws:route_table:vpc-0:subnet-1-route_table
+        - aws:route_table:vpc-0:subnet-2-route_table
+        - aws:route_table:vpc-0:subnet-3-route_table
+        - aws:security_group:vpc-0:eks_cluster-0-security_group
+        - aws:target_group:rest-api-4-integbcc77100
+        - aws:vpc_link:rest_api_4_integration_0-pod2
+        - kubernetes:service:eks_cluster-0:restapi4integration0-pod2
+        - kubernetes:service_account:eks_cluster-0:aws-load-balancer-controller
+        - kubernetes:service_account:eks_cluster-0:pod2
+        - kubernetes:target_group_binding:eks_cluster-0:restapi4integration0-pod2
     parent: eks_cluster/eks_cluster-0
-
+    tag: big
 
   kubernetes:helm_chart:eks_cluster-0/aws-load-balancer-controller:
+    children:
+        - aws:api_deployment:rest_api_4:api_deployment-0
+        - aws:api_method:rest_api_4:rest_api_4_integration_0_method
+        - aws:api_resource:rest_api_4:api_resource-0
+        - aws:api_stage:rest_api_4:api_stage-0
+        - aws:eks_node_group:eks_node_group-0
+        - aws:elastic_ip:subnet-0-route_table-nat_gateway-elastic_ip
+        - aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip
+        - aws:iam_oidc_provider:eks_cluster-0
+        - aws:iam_policy:iam_policy-0
+        - aws:iam_role:aws-load-balancer-controller
+        - aws:iam_role:eks_node_group-0-iam_role
+        - aws:iam_role:pod2
+        - aws:internet_gateway:vpc-0:internet_gateway-0
+        - aws:load_balancer_listener:rest_api_4_integration_0-pod2
+        - aws:nat_gateway:subnet-2:subnet-0-route_table-nat_gateway
+        - aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway
+        - aws:route_table:vpc-0:subnet-0-route_table
+        - aws:route_table:vpc-0:subnet-1-route_table
+        - aws:route_table:vpc-0:subnet-2-route_table
+        - aws:route_table:vpc-0:subnet-3-route_table
+        - aws:security_group:vpc-0:eks_cluster-0-security_group
+        - aws:target_group:rest-api-4-integbcc77100
+        - aws:vpc_link:rest_api_4_integration_0-pod2
+        - kubernetes:service:eks_cluster-0:restapi4integration0-pod2
+        - kubernetes:service_account:eks_cluster-0:aws-load-balancer-controller
+        - kubernetes:service_account:eks_cluster-0:pod2
+        - kubernetes:target_group_binding:eks_cluster-0:restapi4integration0-pod2
     parent: eks_cluster/eks_cluster-0
-
+    tag: big
 
   vpc/vpc-0:
+    children:
+        - aws:api_deployment:rest_api_4:api_deployment-0
+        - aws:api_method:rest_api_4:rest_api_4_integration_0_method
+        - aws:api_resource:rest_api_4:api_resource-0
+        - aws:api_stage:rest_api_4:api_stage-0
+        - aws:eks_node_group:eks_node_group-0
+        - aws:elastic_ip:subnet-0-route_table-nat_gateway-elastic_ip
+        - aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip
+        - aws:iam_oidc_provider:eks_cluster-0
+        - aws:iam_policy:iam_policy-0
+        - aws:iam_role:aws-load-balancer-controller
+        - aws:iam_role:eks_node_group-0-iam_role
+        - aws:iam_role:pod2
+        - aws:internet_gateway:vpc-0:internet_gateway-0
+        - aws:load_balancer_listener:rest_api_4_integration_0-pod2
+        - aws:nat_gateway:subnet-2:subnet-0-route_table-nat_gateway
+        - aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway
+        - aws:route_table:vpc-0:subnet-0-route_table
+        - aws:route_table:vpc-0:subnet-1-route_table
+        - aws:route_table:vpc-0:subnet-2-route_table
+        - aws:route_table:vpc-0:subnet-3-route_table
+        - aws:security_group:vpc-0:eks_cluster-0-security_group
+        - aws:target_group:rest-api-4-integbcc77100
+        - aws:vpc_link:rest_api_4_integration_0-pod2
+        - kubernetes:service:eks_cluster-0:restapi4integration0-pod2
+        - kubernetes:service_account:eks_cluster-0:aws-load-balancer-controller
+        - kubernetes:service_account:eks_cluster-0:pod2
+        - kubernetes:target_group_binding:eks_cluster-0:restapi4integration0-pod2
+    tag: parent
 
   rest_api/rest_api_4:
+    children:
+        - aws:api_stage:rest_api_4:api_stage-0
+    tag: parent
 
   eks_cluster/eks_cluster-0:
-    children: aws:iam_role:ClusterRole-eks_cluster-0,aws:security_group:vpc-0:eks_cluster-0-security_group
-    parent: vpc/vpc-0
-
+    children:
+        - aws:eks_add_on:vpc-cni
+        - aws:eks_node_group:eks_node_group-0
+        - aws:iam_oidc_provider:eks_cluster-0
+        - aws:iam_policy:iam_policy-0
+        - aws:iam_role:ClusterRole-eks_cluster-0
+        - aws:iam_role:aws-load-balancer-controller
+        - aws:iam_role:eks_node_group-0-iam_role
+        - aws:iam_role:pod2
+        - aws:security_group:vpc-0:eks_cluster-0-security_group
+        - aws:target_group:rest-api-4-integbcc77100
+        - kubernetes:config_map:fluent-bit-cluster-info
+        - kubernetes:manifest:fluent-bit
+        - kubernetes:namespace:amazon-cloudwatch
+        - kubernetes:service:eks_cluster-0:restapi4integration0-pod2
+        - kubernetes:service_account:eks_cluster-0:aws-load-balancer-controller
+        - kubernetes:service_account:eks_cluster-0:pod2
+        - kubernetes:target_group_binding:eks_cluster-0:restapi4integration0-pod2
+    tag: parent
 
   aws:api_integration:rest_api_4/rest_api_4_integration_0:
+    children:
+        - aws:api_deployment:rest_api_4:api_deployment-0
+        - aws:api_method:rest_api_4:rest_api_4_integration_0_method
+        - aws:api_resource:rest_api_4:api_resource-0
+        - aws:api_stage:rest_api_4:api_stage-0
+        - aws:eks_node_group:eks_node_group-0
+        - aws:elastic_ip:subnet-0-route_table-nat_gateway-elastic_ip
+        - aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip
+        - aws:iam_oidc_provider:eks_cluster-0
+        - aws:iam_policy:iam_policy-0
+        - aws:iam_role:aws-load-balancer-controller
+        - aws:iam_role:eks_node_group-0-iam_role
+        - aws:iam_role:pod2
+        - aws:internet_gateway:vpc-0:internet_gateway-0
+        - aws:load_balancer_listener:rest_api_4_integration_0-pod2
+        - aws:nat_gateway:subnet-2:subnet-0-route_table-nat_gateway
+        - aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway
+        - aws:route_table:vpc-0:subnet-0-route_table
+        - aws:route_table:vpc-0:subnet-1-route_table
+        - aws:route_table:vpc-0:subnet-2-route_table
+        - aws:route_table:vpc-0:subnet-3-route_table
+        - aws:security_group:vpc-0:eks_cluster-0-security_group
+        - aws:target_group:rest-api-4-integbcc77100
+        - aws:vpc_link:rest_api_4_integration_0-pod2
+        - kubernetes:service:eks_cluster-0:restapi4integration0-pod2
+        - kubernetes:service_account:eks_cluster-0:aws-load-balancer-controller
+        - kubernetes:service_account:eks_cluster-0:pod2
+        - kubernetes:target_group_binding:eks_cluster-0:restapi4integration0-pod2
     parent: rest_api/rest_api_4
+    tag: big
 
   aws:api_integration:rest_api_4/rest_api_4_integration_0 -> load_balancer/rest-api-4-integbcc77100:
-    path: aws:vpc_link:rest_api_4_integration_0-pod2
-
-
diff --git a/pkg/engine2/testdata/k8s_api.expect.yaml b/pkg/engine2/testdata/k8s_api.expect.yaml
index 224983cf..118e19b9 100755
--- a/pkg/engine2/testdata/k8s_api.expect.yaml
+++ b/pkg/engine2/testdata/k8s_api.expect.yaml
@@ -283,12 +283,12 @@ resources:
                         - ec2.amazonaws.com
             Version: "2012-10-17"
         ManagedPolicies:
-            - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy
-            - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
             - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
             - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
             - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
             - arn:aws:iam::aws:policy/AWSCloudMapFullAccess
+            - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy
+            - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
     aws:iam_role:pod2:
         AssumeRolePolicyDoc:
             Statement:
@@ -499,12 +499,6 @@ resources:
               Protocol: "-1"
               ToPort: 0
         IngressRules:
-            - CidrBlocks:
-                - 10.0.192.0/18
-              Description: Allow ingress traffic from ip addresses within the subnet subnet-1
-              FromPort: 0
-              Protocol: "-1"
-              ToPort: 0
             - CidrBlocks:
                 - 0.0.0.0/0
               Description: Allows ingress traffic from the EKS control plane
@@ -522,6 +516,12 @@ resources:
               FromPort: 0
               Protocol: "-1"
               ToPort: 0
+            - CidrBlocks:
+                - 10.0.192.0/18
+              Description: Allow ingress traffic from ip addresses within the subnet subnet-1
+              FromPort: 0
+              Protocol: "-1"
+              ToPort: 0
         Vpc: aws:vpc:vpc-0
     aws:route_table:vpc-0:subnet-0-route_table:
         Routes:
diff --git a/pkg/engine2/testdata/k8s_api.iac-viz.yaml b/pkg/engine2/testdata/k8s_api.iac-viz.yaml
index 0a18c2cd..2798df59 100755
--- a/pkg/engine2/testdata/k8s_api.iac-viz.yaml
+++ b/pkg/engine2/testdata/k8s_api.iac-viz.yaml
@@ -5,37 +5,37 @@ resources:
   vpc/vpc-0:
 
   aws:availability_zone:region-0/availability_zone-0:
-  aws:availability_zone:region-0/availability_zone-0 -> region/region-0:
 
+  aws:availability_zone:region-0/availability_zone-0 -> region/region-0:
   aws:security_group:vpc-0/eks_cluster-0-security_group:
-  aws:security_group:vpc-0/eks_cluster-0-security_group -> vpc/vpc-0:
 
+  aws:security_group:vpc-0/eks_cluster-0-security_group -> vpc/vpc-0:
   aws:availability_zone:region-0/availability_zone-1:
-  aws:availability_zone:region-0/availability_zone-1 -> region/region-0:
 
+  aws:availability_zone:region-0/availability_zone-1 -> region/region-0:
   iam_role/clusterrole-eks_cluster-0:
 
   aws:subnet:vpc-0/subnet-0:
+
   aws:subnet:vpc-0/subnet-0 -> aws:availability_zone:region-0/availability_zone-0:
   aws:subnet:vpc-0/subnet-0 -> aws:security_group:vpc-0/eks_cluster-0-security_group:
   aws:subnet:vpc-0/subnet-0 -> vpc/vpc-0:
-
   aws:subnet:vpc-0/subnet-1:
+
   aws:subnet:vpc-0/subnet-1 -> aws:availability_zone:region-0/availability_zone-1:
   aws:subnet:vpc-0/subnet-1 -> aws:security_group:vpc-0/eks_cluster-0-security_group:
   aws:subnet:vpc-0/subnet-1 -> vpc/vpc-0:
-
   eks_cluster/eks_cluster-0:
+
   eks_cluster/eks_cluster-0 -> iam_role/clusterrole-eks_cluster-0:
   eks_cluster/eks_cluster-0 -> aws:security_group:vpc-0/eks_cluster-0-security_group:
   eks_cluster/eks_cluster-0 -> aws:subnet:vpc-0/subnet-0:
   eks_cluster/eks_cluster-0 -> aws:subnet:vpc-0/subnet-1:
   eks_cluster/eks_cluster-0 -> vpc/vpc-0:
-
   iam_oidc_provider/eks_cluster-0:
+
   iam_oidc_provider/eks_cluster-0 -> eks_cluster/eks_cluster-0:
   iam_oidc_provider/eks_cluster-0 -> region/region-0:
-
   rest_api/rest_api_4:
 
   ecr_repo/ecr_repo-0:
@@ -43,169 +43,169 @@ resources:
   iam_role/eks_node_group-0-iam_role:
 
   iam_role/pod2:
-  iam_role/pod2 -> iam_oidc_provider/eks_cluster-0:
 
+  iam_role/pod2 -> iam_oidc_provider/eks_cluster-0:
   aws:api_resource:rest_api_4/api_resource-0:
-  aws:api_resource:rest_api_4/api_resource-0 -> rest_api/rest_api_4:
 
+  aws:api_resource:rest_api_4/api_resource-0 -> rest_api/rest_api_4:
   load_balancer/rest-api-4-integbcc77100:
+
   load_balancer/rest-api-4-integbcc77100 -> aws:subnet:vpc-0/subnet-0:
   load_balancer/rest-api-4-integbcc77100 -> aws:subnet:vpc-0/subnet-1:
-
   iam_role/aws-load-balancer-controller:
-  iam_role/aws-load-balancer-controller -> iam_oidc_provider/eks_cluster-0:
 
+  iam_role/aws-load-balancer-controller -> iam_oidc_provider/eks_cluster-0:
   ecr_image/pod2-ecr_image:
-  ecr_image/pod2-ecr_image -> ecr_repo/ecr_repo-0:
 
+  ecr_image/pod2-ecr_image -> ecr_repo/ecr_repo-0:
   eks_node_group/eks_node_group-0:
+
   eks_node_group/eks_node_group-0 -> eks_cluster/eks_cluster-0:
   eks_node_group/eks_node_group-0 -> iam_role/eks_node_group-0-iam_role:
   eks_node_group/eks_node_group-0 -> aws:subnet:vpc-0/subnet-0:
   eks_node_group/eks_node_group-0 -> aws:subnet:vpc-0/subnet-1:
-
   kubernetes:service_account:eks_cluster-0/pod2:
+
   kubernetes:service_account:eks_cluster-0/pod2 -> eks_cluster/eks_cluster-0:
   kubernetes:service_account:eks_cluster-0/pod2 -> iam_role/pod2:
-
   elastic_ip/subnet-1-route_table-nat_gateway-elastic_ip:
 
   aws:subnet:vpc-0/subnet-3:
+
   aws:subnet:vpc-0/subnet-3 -> aws:availability_zone:region-0/availability_zone-1:
   aws:subnet:vpc-0/subnet-3 -> vpc/vpc-0:
-
   elastic_ip/subnet-0-route_table-nat_gateway-elastic_ip:
 
   aws:subnet:vpc-0/subnet-2:
+
   aws:subnet:vpc-0/subnet-2 -> aws:availability_zone:region-0/availability_zone-0:
   aws:subnet:vpc-0/subnet-2 -> vpc/vpc-0:
-
   aws:api_method:rest_api_4/rest_api_4_integration_0_method:
+
   aws:api_method:rest_api_4/rest_api_4_integration_0_method -> aws:api_resource:rest_api_4/api_resource-0:
   aws:api_method:rest_api_4/rest_api_4_integration_0_method -> rest_api/rest_api_4:
-
   vpc_link/rest_api_4_integration_0-pod2:
-  vpc_link/rest_api_4_integration_0-pod2 -> load_balancer/rest-api-4-integbcc77100:
 
+  vpc_link/rest_api_4_integration_0-pod2 -> load_balancer/rest-api-4-integbcc77100:
   kubernetes:service_account:eks_cluster-0/aws-load-balancer-controller:
+
   kubernetes:service_account:eks_cluster-0/aws-load-balancer-controller -> eks_cluster/eks_cluster-0:
   kubernetes:service_account:eks_cluster-0/aws-load-balancer-controller -> iam_role/aws-load-balancer-controller:
-
   kubernetes:pod:eks_cluster-0/pod2:
+
   kubernetes:pod:eks_cluster-0/pod2 -> ecr_image/pod2-ecr_image:
   kubernetes:pod:eks_cluster-0/pod2 -> eks_cluster/eks_cluster-0:
   kubernetes:pod:eks_cluster-0/pod2 -> eks_node_group/eks_node_group-0:
   kubernetes:pod:eks_cluster-0/pod2 -> kubernetes:service_account:eks_cluster-0/pod2:
-
   kubernetes:namespace/amazon-cloudwatch:
-  kubernetes:namespace/amazon-cloudwatch -> eks_cluster/eks_cluster-0:
 
+  kubernetes:namespace/amazon-cloudwatch -> eks_cluster/eks_cluster-0:
   aws:internet_gateway:vpc-0/internet_gateway-0:
-  aws:internet_gateway:vpc-0/internet_gateway-0 -> vpc/vpc-0:
 
+  aws:internet_gateway:vpc-0/internet_gateway-0 -> vpc/vpc-0:
   aws:nat_gateway:subnet-3/subnet-1-route_table-nat_gateway:
+
   aws:nat_gateway:subnet-3/subnet-1-route_table-nat_gateway -> elastic_ip/subnet-1-route_table-nat_gateway-elastic_ip:
   aws:nat_gateway:subnet-3/subnet-1-route_table-nat_gateway -> aws:subnet:vpc-0/subnet-3:
-
   aws:nat_gateway:subnet-2/subnet-0-route_table-nat_gateway:
+
   aws:nat_gateway:subnet-2/subnet-0-route_table-nat_gateway -> elastic_ip/subnet-0-route_table-nat_gateway-elastic_ip:
   aws:nat_gateway:subnet-2/subnet-0-route_table-nat_gateway -> aws:subnet:vpc-0/subnet-2:
-
   aws:api_integration:rest_api_4/rest_api_4_integration_0:
+
   aws:api_integration:rest_api_4/rest_api_4_integration_0 -> aws:api_method:rest_api_4/rest_api_4_integration_0_method:
   aws:api_integration:rest_api_4/rest_api_4_integration_0 -> aws:api_resource:rest_api_4/api_resource-0:
   aws:api_integration:rest_api_4/rest_api_4_integration_0 -> load_balancer/rest-api-4-integbcc77100:
   aws:api_integration:rest_api_4/rest_api_4_integration_0 -> rest_api/rest_api_4:
   aws:api_integration:rest_api_4/rest_api_4_integration_0 -> vpc_link/rest_api_4_integration_0-pod2:
-
   target_group/rest-api-4-integbcc77100:
-  target_group/rest-api-4-integbcc77100 -> vpc/vpc-0:
 
+  target_group/rest-api-4-integbcc77100 -> vpc/vpc-0:
   kubernetes:helm_chart:eks_cluster-0/aws-load-balancer-controller:
+
   kubernetes:helm_chart:eks_cluster-0/aws-load-balancer-controller -> eks_cluster/eks_cluster-0:
   kubernetes:helm_chart:eks_cluster-0/aws-load-balancer-controller -> region/region-0:
   kubernetes:helm_chart:eks_cluster-0/aws-load-balancer-controller -> kubernetes:service_account:eks_cluster-0/aws-load-balancer-controller:
-
   kubernetes:service:eks_cluster-0/restapi4integration0-pod2:
+
   kubernetes:service:eks_cluster-0/restapi4integration0-pod2 -> eks_cluster/eks_cluster-0:
   kubernetes:service:eks_cluster-0/restapi4integration0-pod2 -> kubernetes:pod:eks_cluster-0/pod2:
-
   kubernetes:config_map/fluent-bit-cluster-info:
+
   kubernetes:config_map/fluent-bit-cluster-info -> eks_cluster/eks_cluster-0:
   kubernetes:config_map/fluent-bit-cluster-info -> kubernetes:namespace/amazon-cloudwatch:
-
   aws:route_table:vpc-0/subnet-3-route_table:
+
   aws:route_table:vpc-0/subnet-3-route_table -> aws:internet_gateway:vpc-0/internet_gateway-0:
   aws:route_table:vpc-0/subnet-3-route_table -> vpc/vpc-0:
-
   aws:route_table:vpc-0/subnet-2-route_table:
+
   aws:route_table:vpc-0/subnet-2-route_table -> aws:internet_gateway:vpc-0/internet_gateway-0:
   aws:route_table:vpc-0/subnet-2-route_table -> vpc/vpc-0:
-
   aws:route_table:vpc-0/subnet-1-route_table:
+
   aws:route_table:vpc-0/subnet-1-route_table -> aws:nat_gateway:subnet-3/subnet-1-route_table-nat_gateway:
   aws:route_table:vpc-0/subnet-1-route_table -> vpc/vpc-0:
-
   aws:route_table:vpc-0/subnet-0-route_table:
+
   aws:route_table:vpc-0/subnet-0-route_table -> aws:nat_gateway:subnet-2/subnet-0-route_table-nat_gateway:
   aws:route_table:vpc-0/subnet-0-route_table -> vpc/vpc-0:
-
   iam_policy/iam_policy-0:
 
   aws:api_deployment:rest_api_4/api_deployment-0:
+
   aws:api_deployment:rest_api_4/api_deployment-0 -> aws:api_integration:rest_api_4/rest_api_4_integration_0:
   aws:api_deployment:rest_api_4/api_deployment-0 -> aws:api_method:rest_api_4/rest_api_4_integration_0_method:
   aws:api_deployment:rest_api_4/api_deployment-0 -> rest_api/rest_api_4:
-
   kubernetes:target_group_binding:eks_cluster-0/restapi4integration0-pod2:
+
   kubernetes:target_group_binding:eks_cluster-0/restapi4integration0-pod2 -> eks_cluster/eks_cluster-0:
   kubernetes:target_group_binding:eks_cluster-0/restapi4integration0-pod2 -> target_group/rest-api-4-integbcc77100:
   kubernetes:target_group_binding:eks_cluster-0/restapi4integration0-pod2 -> kubernetes:helm_chart:eks_cluster-0/aws-load-balancer-controller:
   kubernetes:target_group_binding:eks_cluster-0/restapi4integration0-pod2 -> kubernetes:service:eks_cluster-0/restapi4integration0-pod2:
-
   kubernetes:manifest/fluent-bit:
+
   kubernetes:manifest/fluent-bit -> eks_cluster/eks_cluster-0:
   kubernetes:manifest/fluent-bit -> kubernetes:config_map/fluent-bit-cluster-info:
-
   kubernetes:kube_config/eks_cluster-0-kube_config:
+
   kubernetes:kube_config/eks_cluster-0-kube_config -> eks_cluster/eks_cluster-0:
   kubernetes:kube_config/eks_cluster-0-kube_config -> region/region-0:
-
   kubernetes:helm_chart:eks_cluster-0/metricsserver:
+
   kubernetes:helm_chart:eks_cluster-0/metricsserver -> eks_cluster/eks_cluster-0:
   kubernetes:helm_chart:eks_cluster-0/metricsserver -> eks_node_group/eks_node_group-0:
-
   security_group_rule/security_group_rule-0:
-  security_group_rule/security_group_rule-0 -> vpc/vpc-0:
 
+  security_group_rule/security_group_rule-0 -> vpc/vpc-0:
   route_table_association/subnet-3-subnet-3-route_table:
+
   route_table_association/subnet-3-subnet-3-route_table -> aws:route_table:vpc-0/subnet-3-route_table:
   route_table_association/subnet-3-subnet-3-route_table -> aws:subnet:vpc-0/subnet-3:
-
   route_table_association/subnet-2-subnet-2-route_table:
+
   route_table_association/subnet-2-subnet-2-route_table -> aws:route_table:vpc-0/subnet-2-route_table:
   route_table_association/subnet-2-subnet-2-route_table -> aws:subnet:vpc-0/subnet-2:
-
   route_table_association/subnet-1-subnet-1-route_table:
+
   route_table_association/subnet-1-subnet-1-route_table -> aws:route_table:vpc-0/subnet-1-route_table:
   route_table_association/subnet-1-subnet-1-route_table -> aws:subnet:vpc-0/subnet-1:
-
   route_table_association/subnet-0-subnet-0-route_table:
+
   route_table_association/subnet-0-subnet-0-route_table -> aws:route_table:vpc-0/subnet-0-route_table:
   route_table_association/subnet-0-subnet-0-route_table -> aws:subnet:vpc-0/subnet-0:
-
   load_balancer_listener/rest_api_4_integration_0-pod2:
+
   load_balancer_listener/rest_api_4_integration_0-pod2 -> load_balancer/rest-api-4-integbcc77100:
   load_balancer_listener/rest_api_4_integration_0-pod2 -> target_group/rest-api-4-integbcc77100:
-
   iam_role_policy_attachment/aws-load-balancer-controller-iam_policy-0:
+
   iam_role_policy_attachment/aws-load-balancer-controller-iam_policy-0 -> iam_policy/iam_policy-0:
   iam_role_policy_attachment/aws-load-balancer-controller-iam_policy-0 -> iam_role/aws-load-balancer-controller:
-
   eks_add_on/vpc-cni:
-  eks_add_on/vpc-cni -> eks_cluster/eks_cluster-0:
 
+  eks_add_on/vpc-cni -> eks_cluster/eks_cluster-0:
   aws:api_stage:rest_api_4/api_stage-0:
+
   aws:api_stage:rest_api_4/api_stage-0 -> aws:api_deployment:rest_api_4/api_deployment-0:
   aws:api_stage:rest_api_4/api_stage-0 -> rest_api/rest_api_4:
-
diff --git a/pkg/engine2/testdata/lambda_efs.dataflow-viz.yaml b/pkg/engine2/testdata/lambda_efs.dataflow-viz.yaml
index a75e510a..90e7b643 100755
--- a/pkg/engine2/testdata/lambda_efs.dataflow-viz.yaml
+++ b/pkg/engine2/testdata/lambda_efs.dataflow-viz.yaml
@@ -1,14 +1,41 @@
 provider: aws
 resources:
   efs_file_system/test-efs-fs:
+    children:
+        - aws:efs_access_point:test-efs-fs:lambda_test_app-test-efs-fs
+        - aws:efs_mount_target:test-efs-fs:lambda_test_app-test-efs-fs
+        - aws:efs_mount_target:test-efs-fs:subnet-1-test-efs-fs
+        - aws:iam_role:lambda_test_app-ExecutionRole
+        - aws:log_group:lambda_test_app-log-group
+    parent: vpc/vpc-0
+    tag: big
 
   vpc/vpc-0:
+    children:
+        - aws:efs_mount_target:test-efs-fs:lambda_test_app-test-efs-fs
+        - aws:efs_mount_target:test-efs-fs:subnet-1-test-efs-fs
+        - aws:elastic_ip:lambda_test_app-test-efs-fs-route_table-nat_gateway-elastic_ip
+        - aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip
+        - aws:iam_role:lambda_test_app-ExecutionRole
+        - aws:internet_gateway:vpc-0:internet_gateway-0
+        - aws:log_group:lambda_test_app-log-group
+        - aws:nat_gateway:subnet-2:lambda_test_app-test-efs-fs-route_table-nat_gateway
+        - aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway
+        - aws:route_table:vpc-0:lambda_test_app-test-efs-fs-route_table
+        - aws:route_table:vpc-0:subnet-1-route_table
+        - aws:route_table:vpc-0:subnet-2-route_table
+        - aws:route_table:vpc-0:subnet-3-route_table
+        - aws:security_group:vpc-0:lambda_test_app-security_group
+        - aws:security_group:vpc-0:lambda_test_app-test-efs-fs
+        - aws:security_group:vpc-0:subnet-1-test-efs-fs
+    tag: parent
 
   lambda_function/lambda_test_app:
-    children: aws:ecr_image:lambda_test_app-image,aws:iam_role:lambda_test_app-ExecutionRole,aws:log_group:lambda_test_app-log-group
+    children:
+        - aws:ecr_image:lambda_test_app-image
+        - aws:ecr_repo:ecr_repo-0
+        - aws:security_group:vpc-0:lambda_test_app-security_group
     parent: vpc/vpc-0
+    tag: big
 
   lambda_function/lambda_test_app -> efs_file_system/test-efs-fs:
-    path: aws:efs_access_point:test-efs-fs:lambda_test_app-test-efs-fs
-
-
diff --git a/pkg/engine2/testdata/lambda_efs.expect.yaml b/pkg/engine2/testdata/lambda_efs.expect.yaml
index da403800..7245f91b 100755
--- a/pkg/engine2/testdata/lambda_efs.expect.yaml
+++ b/pkg/engine2/testdata/lambda_efs.expect.yaml
@@ -62,8 +62,8 @@ resources:
                         - aws:efs_file_system:test-efs-fs#Arn
                 Version: "2012-10-17"
         ManagedPolicies:
-            - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
             - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
+            - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
     aws:ecr_repo:ecr_repo-0:
         ForceDelete: true
     aws:efs_mount_target:test-efs-fs:lambda_test_app-test-efs-fs:
diff --git a/pkg/engine2/testdata/lambda_efs.iac-viz.yaml b/pkg/engine2/testdata/lambda_efs.iac-viz.yaml
index 969dafc2..62e6bd8b 100755
--- a/pkg/engine2/testdata/lambda_efs.iac-viz.yaml
+++ b/pkg/engine2/testdata/lambda_efs.iac-viz.yaml
@@ -3,120 +3,120 @@ resources:
   region/region-0:
 
   aws:availability_zone:region-0/availability_zone-1:
-  aws:availability_zone:region-0/availability_zone-1 -> region/region-0:
 
+  aws:availability_zone:region-0/availability_zone-1 -> region/region-0:
   vpc/vpc-0:
 
   aws:availability_zone:region-0/availability_zone-0:
-  aws:availability_zone:region-0/availability_zone-0 -> region/region-0:
 
+  aws:availability_zone:region-0/availability_zone-0 -> region/region-0:
   elastic_ip/subnet-1-route_table-nat_gateway-elastic_ip:
 
   aws:subnet:vpc-0/subnet-3:
+
   aws:subnet:vpc-0/subnet-3 -> aws:availability_zone:region-0/availability_zone-1:
   aws:subnet:vpc-0/subnet-3 -> vpc/vpc-0:
-
   elastic_ip/lambda_test_app-test-efs-fs-route_table-nat_gateway-elastic_ip:
 
   aws:subnet:vpc-0/subnet-2:
+
   aws:subnet:vpc-0/subnet-2 -> aws:availability_zone:region-0/availability_zone-0:
   aws:subnet:vpc-0/subnet-2 -> vpc/vpc-0:
-
   aws:internet_gateway:vpc-0/internet_gateway-0:
-  aws:internet_gateway:vpc-0/internet_gateway-0 -> vpc/vpc-0:
 
+  aws:internet_gateway:vpc-0/internet_gateway-0 -> vpc/vpc-0:
   aws:nat_gateway:subnet-3/subnet-1-route_table-nat_gateway:
+
   aws:nat_gateway:subnet-3/subnet-1-route_table-nat_gateway -> elastic_ip/subnet-1-route_table-nat_gateway-elastic_ip:
   aws:nat_gateway:subnet-3/subnet-1-route_table-nat_gateway -> aws:subnet:vpc-0/subnet-3:
-
   aws:security_group:vpc-0/lambda_test_app-test-efs-fs:
-  aws:security_group:vpc-0/lambda_test_app-test-efs-fs -> vpc/vpc-0:
 
+  aws:security_group:vpc-0/lambda_test_app-test-efs-fs -> vpc/vpc-0:
   aws:security_group:vpc-0/subnet-1-test-efs-fs:
-  aws:security_group:vpc-0/subnet-1-test-efs-fs -> vpc/vpc-0:
 
+  aws:security_group:vpc-0/subnet-1-test-efs-fs -> vpc/vpc-0:
   aws:nat_gateway:subnet-2/lambda_test_app-test-efs-fs-route_table-nat_gateway:
+
   aws:nat_gateway:subnet-2/lambda_test_app-test-efs-fs-route_table-nat_gateway -> elastic_ip/lambda_test_app-test-efs-fs-route_table-nat_gateway-elastic_ip:
   aws:nat_gateway:subnet-2/lambda_test_app-test-efs-fs-route_table-nat_gateway -> aws:subnet:vpc-0/subnet-2:
-
   ecr_repo/ecr_repo-0:
 
   efs_file_system/test-efs-fs:
-  efs_file_system/test-efs-fs -> aws:availability_zone:region-0/availability_zone-0:
 
+  efs_file_system/test-efs-fs -> aws:availability_zone:region-0/availability_zone-0:
   log_group/lambda_test_app-log-group:
 
   aws:route_table:vpc-0/subnet-3-route_table:
+
   aws:route_table:vpc-0/subnet-3-route_table -> aws:internet_gateway:vpc-0/internet_gateway-0:
   aws:route_table:vpc-0/subnet-3-route_table -> vpc/vpc-0:
-
   aws:route_table:vpc-0/subnet-2-route_table:
+
   aws:route_table:vpc-0/subnet-2-route_table -> aws:internet_gateway:vpc-0/internet_gateway-0:
   aws:route_table:vpc-0/subnet-2-route_table -> vpc/vpc-0:
-
   aws:route_table:vpc-0/subnet-1-route_table:
+
   aws:route_table:vpc-0/subnet-1-route_table -> aws:nat_gateway:subnet-3/subnet-1-route_table-nat_gateway:
   aws:route_table:vpc-0/subnet-1-route_table -> vpc/vpc-0:
-
   aws:subnet:vpc-0/subnet-1:
+
   aws:subnet:vpc-0/subnet-1 -> aws:availability_zone:region-0/availability_zone-0:
   aws:subnet:vpc-0/subnet-1 -> aws:security_group:vpc-0/lambda_test_app-test-efs-fs:
   aws:subnet:vpc-0/subnet-1 -> aws:security_group:vpc-0/subnet-1-test-efs-fs:
   aws:subnet:vpc-0/subnet-1 -> vpc/vpc-0:
-
   aws:route_table:vpc-0/lambda_test_app-test-efs-fs-route_table:
+
   aws:route_table:vpc-0/lambda_test_app-test-efs-fs-route_table -> aws:nat_gateway:subnet-2/lambda_test_app-test-efs-fs-route_table-nat_gateway:
   aws:route_table:vpc-0/lambda_test_app-test-efs-fs-route_table -> vpc/vpc-0:
-
   aws:subnet:vpc-0/lambda_test_app-test-efs-fs:
+
   aws:subnet:vpc-0/lambda_test_app-test-efs-fs -> aws:availability_zone:region-0/availability_zone-1:
   aws:subnet:vpc-0/lambda_test_app-test-efs-fs -> aws:security_group:vpc-0/lambda_test_app-test-efs-fs:
   aws:subnet:vpc-0/lambda_test_app-test-efs-fs -> vpc/vpc-0:
-
   ecr_image/lambda_test_app-image:
-  ecr_image/lambda_test_app-image -> ecr_repo/ecr_repo-0:
 
+  ecr_image/lambda_test_app-image -> ecr_repo/ecr_repo-0:
   aws:efs_access_point:test-efs-fs/lambda_test_app-test-efs-fs:
-  aws:efs_access_point:test-efs-fs/lambda_test_app-test-efs-fs -> efs_file_system/test-efs-fs:
 
+  aws:efs_access_point:test-efs-fs/lambda_test_app-test-efs-fs -> efs_file_system/test-efs-fs:
   iam_role/lambda_test_app-executionrole:
+
   iam_role/lambda_test_app-executionrole -> efs_file_system/test-efs-fs:
   iam_role/lambda_test_app-executionrole -> log_group/lambda_test_app-log-group:
-
   aws:security_group:vpc-0/lambda_test_app-security_group:
-  aws:security_group:vpc-0/lambda_test_app-security_group -> vpc/vpc-0:
 
+  aws:security_group:vpc-0/lambda_test_app-security_group -> vpc/vpc-0:
   route_table_association/subnet-3-subnet-3-route_table:
+
   route_table_association/subnet-3-subnet-3-route_table -> aws:route_table:vpc-0/subnet-3-route_table:
   route_table_association/subnet-3-subnet-3-route_table -> aws:subnet:vpc-0/subnet-3:
-
   route_table_association/subnet-2-subnet-2-route_table:
+
   route_table_association/subnet-2-subnet-2-route_table -> aws:route_table:vpc-0/subnet-2-route_table:
   route_table_association/subnet-2-subnet-2-route_table -> aws:subnet:vpc-0/subnet-2:
-
   route_table_association/subnet-1-subnet-1-route_table:
+
   route_table_association/subnet-1-subnet-1-route_table -> aws:route_table:vpc-0/subnet-1-route_table:
   route_table_association/subnet-1-subnet-1-route_table -> aws:subnet:vpc-0/subnet-1:
-
   route_table_association/lambda_test_app-test-efs-fs-lambda_test_app-test-efs-fs-route_table:
+
   route_table_association/lambda_test_app-test-efs-fs-lambda_test_app-test-efs-fs-route_table -> aws:route_table:vpc-0/lambda_test_app-test-efs-fs-route_table:
   route_table_association/lambda_test_app-test-efs-fs-lambda_test_app-test-efs-fs-route_table -> aws:subnet:vpc-0/lambda_test_app-test-efs-fs:
-
   lambda_function/lambda_test_app:
+
   lambda_function/lambda_test_app -> ecr_image/lambda_test_app-image:
   lambda_function/lambda_test_app -> aws:efs_access_point:test-efs-fs/lambda_test_app-test-efs-fs:
   lambda_function/lambda_test_app -> iam_role/lambda_test_app-executionrole:
   lambda_function/lambda_test_app -> aws:security_group:vpc-0/lambda_test_app-security_group:
   lambda_function/lambda_test_app -> aws:subnet:vpc-0/lambda_test_app-test-efs-fs:
   lambda_function/lambda_test_app -> aws:subnet:vpc-0/subnet-1:
-
   aws:efs_mount_target:test-efs-fs/subnet-1-test-efs-fs:
+
   aws:efs_mount_target:test-efs-fs/subnet-1-test-efs-fs -> efs_file_system/test-efs-fs:
   aws:efs_mount_target:test-efs-fs/subnet-1-test-efs-fs -> aws:security_group:vpc-0/subnet-1-test-efs-fs:
   aws:efs_mount_target:test-efs-fs/subnet-1-test-efs-fs -> aws:subnet:vpc-0/subnet-1:
-
   aws:efs_mount_target:test-efs-fs/lambda_test_app-test-efs-fs:
+
   aws:efs_mount_target:test-efs-fs/lambda_test_app-test-efs-fs -> efs_file_system/test-efs-fs:
   aws:efs_mount_target:test-efs-fs/lambda_test_app-test-efs-fs -> aws:security_group:vpc-0/lambda_test_app-test-efs-fs:
   aws:efs_mount_target:test-efs-fs/lambda_test_app-test-efs-fs -> aws:subnet:vpc-0/lambda_test_app-test-efs-fs:
-
diff --git a/pkg/engine2/testdata/namespace_pathselect.dataflow-viz.yaml b/pkg/engine2/testdata/namespace_pathselect.dataflow-viz.yaml
index 9e8b36e6..3d372719 100755
--- a/pkg/engine2/testdata/namespace_pathselect.dataflow-viz.yaml
+++ b/pkg/engine2/testdata/namespace_pathselect.dataflow-viz.yaml
@@ -1,13 +1,38 @@
 provider: aws
 resources:
   vpc/vpc_1:
+    children:
+        - aws:elastic_ip:lambda_function_2-vpc_1-route_table-nat_gateway-elastic_ip
+        - aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip
+        - aws:iam_role:lambda_function_0-ExecutionRole
+        - aws:iam_role:lambda_function_2-ExecutionRole
+        - aws:internet_gateway:vpc_1:internet_gateway-0
+        - aws:log_group:lambda_function_0-log-group
+        - aws:log_group:lambda_function_2-log-group
+        - aws:nat_gateway:subnet-2:lambda_function_2-vpc_1-route_table-nat_gateway
+        - aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway
+        - aws:route_table:vpc_1:lambda_function_2-vpc_1-route_table
+        - aws:route_table:vpc_1:subnet-1-route_table
+        - aws:route_table:vpc_1:subnet-2-route_table
+        - aws:route_table:vpc_1:subnet-3-route_table
+        - aws:security_group:vpc_1:lambda_function_2-security_group
+    tag: parent
 
   lambda_function/lambda_function_2:
-    children: aws:ecr_image:lambda_function_2-image,aws:iam_role:lambda_function_2-ExecutionRole,aws:log_group:lambda_function_2-log-group
+    children:
+        - aws:ecr_image:lambda_function_2-image
+        - aws:iam_role:lambda_function_2-ExecutionRole
+        - aws:security_group:vpc_1:lambda_function_2-security_group
     parent: vpc/vpc_1
-
+    tag: big
 
   lambda_function/lambda_function_0:
-    children: aws:ecr_image:lambda_function_0-image,aws:iam_role:lambda_function_0-ExecutionRole,aws:log_group:lambda_function_0-log-group
-
+    children:
+        - aws:ecr_image:lambda_function_0-image
+        - aws:ecr_repo:ecr_repo-0
+        - aws:iam_role:lambda_function_0-ExecutionRole
+        - aws:log_group:lambda_function_0-log-group
+        - aws:log_group:lambda_function_2-log-group
+    parent: vpc/vpc_1
+    tag: big
 
diff --git a/pkg/engine2/testdata/namespace_pathselect.expect.yaml b/pkg/engine2/testdata/namespace_pathselect.expect.yaml
index a7167502..d86c6736 100755
--- a/pkg/engine2/testdata/namespace_pathselect.expect.yaml
+++ b/pkg/engine2/testdata/namespace_pathselect.expect.yaml
@@ -62,8 +62,8 @@ resources:
                         - lambda.amazonaws.com
             Version: "2012-10-17"
         ManagedPolicies:
-            - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
             - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
+            - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
     aws:subnet:vpc_1:lambda_function_2-vpc_1:
         AvailabilityZone: aws:availability_zone:region-0:availability_zone-0
         CidrBlock: 10.0.128.0/18
diff --git a/pkg/engine2/testdata/namespace_pathselect.iac-viz.yaml b/pkg/engine2/testdata/namespace_pathselect.iac-viz.yaml
index 0b1173b7..44aff518 100755
--- a/pkg/engine2/testdata/namespace_pathselect.iac-viz.yaml
+++ b/pkg/engine2/testdata/namespace_pathselect.iac-viz.yaml
@@ -3,36 +3,36 @@ resources:
   region/region-0:
 
   aws:availability_zone:region-0/availability_zone-1:
-  aws:availability_zone:region-0/availability_zone-1 -> region/region-0:
 
+  aws:availability_zone:region-0/availability_zone-1 -> region/region-0:
   vpc/vpc_1:
 
   aws:availability_zone:region-0/availability_zone-0:
-  aws:availability_zone:region-0/availability_zone-0 -> region/region-0:
 
+  aws:availability_zone:region-0/availability_zone-0 -> region/region-0:
   elastic_ip/subnet-1-route_table-nat_gateway-elastic_ip:
 
   aws:subnet:vpc_1/subnet-3:
+
   aws:subnet:vpc_1/subnet-3 -> aws:availability_zone:region-0/availability_zone-1:
   aws:subnet:vpc_1/subnet-3 -> vpc/vpc_1:
-
   elastic_ip/lambda_function_2-vpc_1-route_table-nat_gateway-elastic_ip:
 
   aws:subnet:vpc_1/subnet-2:
+
   aws:subnet:vpc_1/subnet-2 -> aws:availability_zone:region-0/availability_zone-0:
   aws:subnet:vpc_1/subnet-2 -> vpc/vpc_1:
-
   aws:internet_gateway:vpc_1/internet_gateway-0:
-  aws:internet_gateway:vpc_1/internet_gateway-0 -> vpc/vpc_1:
 
+  aws:internet_gateway:vpc_1/internet_gateway-0 -> vpc/vpc_1:
   aws:nat_gateway:subnet-3/subnet-1-route_table-nat_gateway:
+
   aws:nat_gateway:subnet-3/subnet-1-route_table-nat_gateway -> elastic_ip/subnet-1-route_table-nat_gateway-elastic_ip:
   aws:nat_gateway:subnet-3/subnet-1-route_table-nat_gateway -> aws:subnet:vpc_1/subnet-3:
-
   aws:nat_gateway:subnet-2/lambda_function_2-vpc_1-route_table-nat_gateway:
+
   aws:nat_gateway:subnet-2/lambda_function_2-vpc_1-route_table-nat_gateway -> elastic_ip/lambda_function_2-vpc_1-route_table-nat_gateway-elastic_ip:
   aws:nat_gateway:subnet-2/lambda_function_2-vpc_1-route_table-nat_gateway -> aws:subnet:vpc_1/subnet-2:
-
   ecr_repo/ecr_repo-0:
 
   log_group/lambda_function_2-log-group:
@@ -40,69 +40,69 @@ resources:
   log_group/lambda_function_0-log-group:
 
   aws:route_table:vpc_1/subnet-3-route_table:
+
   aws:route_table:vpc_1/subnet-3-route_table -> aws:internet_gateway:vpc_1/internet_gateway-0:
   aws:route_table:vpc_1/subnet-3-route_table -> vpc/vpc_1:
-
   aws:route_table:vpc_1/subnet-2-route_table:
+
   aws:route_table:vpc_1/subnet-2-route_table -> aws:internet_gateway:vpc_1/internet_gateway-0:
   aws:route_table:vpc_1/subnet-2-route_table -> vpc/vpc_1:
-
   aws:route_table:vpc_1/subnet-1-route_table:
+
   aws:route_table:vpc_1/subnet-1-route_table -> aws:nat_gateway:subnet-3/subnet-1-route_table-nat_gateway:
   aws:route_table:vpc_1/subnet-1-route_table -> vpc/vpc_1:
-
   aws:subnet:vpc_1/subnet-1:
+
   aws:subnet:vpc_1/subnet-1 -> aws:availability_zone:region-0/availability_zone-1:
   aws:subnet:vpc_1/subnet-1 -> vpc/vpc_1:
-
   aws:route_table:vpc_1/lambda_function_2-vpc_1-route_table:
+
   aws:route_table:vpc_1/lambda_function_2-vpc_1-route_table -> aws:nat_gateway:subnet-2/lambda_function_2-vpc_1-route_table-nat_gateway:
   aws:route_table:vpc_1/lambda_function_2-vpc_1-route_table -> vpc/vpc_1:
-
   aws:subnet:vpc_1/lambda_function_2-vpc_1:
+
   aws:subnet:vpc_1/lambda_function_2-vpc_1 -> aws:availability_zone:region-0/availability_zone-0:
   aws:subnet:vpc_1/lambda_function_2-vpc_1 -> vpc/vpc_1:
-
   ecr_image/lambda_function_2-image:
-  ecr_image/lambda_function_2-image -> ecr_repo/ecr_repo-0:
 
+  ecr_image/lambda_function_2-image -> ecr_repo/ecr_repo-0:
   iam_role/lambda_function_2-executionrole:
-  iam_role/lambda_function_2-executionrole -> log_group/lambda_function_2-log-group:
 
+  iam_role/lambda_function_2-executionrole -> log_group/lambda_function_2-log-group:
   aws:security_group:vpc_1/lambda_function_2-security_group:
-  aws:security_group:vpc_1/lambda_function_2-security_group -> vpc/vpc_1:
 
+  aws:security_group:vpc_1/lambda_function_2-security_group -> vpc/vpc_1:
   ecr_image/lambda_function_0-image:
-  ecr_image/lambda_function_0-image -> ecr_repo/ecr_repo-0:
 
+  ecr_image/lambda_function_0-image -> ecr_repo/ecr_repo-0:
   iam_role/lambda_function_0-executionrole:
-  iam_role/lambda_function_0-executionrole -> log_group/lambda_function_0-log-group:
 
+  iam_role/lambda_function_0-executionrole -> log_group/lambda_function_0-log-group:
   route_table_association/subnet-3-subnet-3-route_table:
+
   route_table_association/subnet-3-subnet-3-route_table -> aws:route_table:vpc_1/subnet-3-route_table:
   route_table_association/subnet-3-subnet-3-route_table -> aws:subnet:vpc_1/subnet-3:
-
   route_table_association/subnet-2-subnet-2-route_table:
+
   route_table_association/subnet-2-subnet-2-route_table -> aws:route_table:vpc_1/subnet-2-route_table:
   route_table_association/subnet-2-subnet-2-route_table -> aws:subnet:vpc_1/subnet-2:
-
   route_table_association/subnet-1-subnet-1-route_table:
+
   route_table_association/subnet-1-subnet-1-route_table -> aws:route_table:vpc_1/subnet-1-route_table:
   route_table_association/subnet-1-subnet-1-route_table -> aws:subnet:vpc_1/subnet-1:
-
   route_table_association/lambda_function_2-vpc_1-lambda_function_2-vpc_1-route_table:
+
   route_table_association/lambda_function_2-vpc_1-lambda_function_2-vpc_1-route_table -> aws:route_table:vpc_1/lambda_function_2-vpc_1-route_table:
   route_table_association/lambda_function_2-vpc_1-lambda_function_2-vpc_1-route_table -> aws:subnet:vpc_1/lambda_function_2-vpc_1:
-
   lambda_function/lambda_function_2:
+
   lambda_function/lambda_function_2 -> ecr_image/lambda_function_2-image:
   lambda_function/lambda_function_2 -> iam_role/lambda_function_2-executionrole:
   lambda_function/lambda_function_2 -> aws:security_group:vpc_1/lambda_function_2-security_group:
   lambda_function/lambda_function_2 -> aws:subnet:vpc_1/lambda_function_2-vpc_1:
   lambda_function/lambda_function_2 -> aws:subnet:vpc_1/subnet-1:
-
   lambda_function/lambda_function_0:
+
   lambda_function/lambda_function_0 -> ecr_image/lambda_function_0-image:
   lambda_function/lambda_function_0 -> iam_role/lambda_function_0-executionrole:
   lambda_function/lambda_function_0 -> log_group/lambda_function_0-log-group:
-
diff --git a/pkg/engine2/testdata/remove_path.dataflow-viz.yaml b/pkg/engine2/testdata/remove_path.dataflow-viz.yaml
index e0356003..83e7a6de 100644
--- a/pkg/engine2/testdata/remove_path.dataflow-viz.yaml
+++ b/pkg/engine2/testdata/remove_path.dataflow-viz.yaml
@@ -1,22 +1,50 @@
 provider: aws
 resources:
   rds_instance/rds-instance-1:
-    children: aws:rds_subnet_group:rds_subnet_group-0,aws:security_group:vpc-0:rds-instance-1-security_group
+    children:
+        - aws:rds_subnet_group:rds_subnet_group-0
+        - aws:security_group:vpc-0:rds-instance-1-security_group
     parent: vpc/vpc-0
-
+    tag: big
 
   vpc/vpc-0:
+    children:
+        - aws:elastic_ip:subnet-0-route_table-nat_gateway-elastic_ip
+        - aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip
+        - aws:iam_role:lambda_function_0-ExecutionRole
+        - aws:iam_role:lambda_function_3-ExecutionRole
+        - aws:internet_gateway:vpc-0:internet_gateway-0
+        - aws:log_group:lambda_function_0-log-group
+        - aws:log_group:lambda_function_3-log-group
+        - aws:nat_gateway:subnet-2:subnet-0-route_table-nat_gateway
+        - aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway
+        - aws:rds_subnet_group:rds_subnet_group-0
+        - aws:route_table:vpc-0:subnet-0-route_table
+        - aws:route_table:vpc-0:subnet-1-route_table
+        - aws:route_table:vpc-0:subnet-2-route_table
+        - aws:route_table:vpc-0:subnet-3-route_table
+        - aws:security_group:vpc-0:lambda_function_0-security_group
+        - aws:security_group:vpc-0:lambda_function_3-security_group
+        - aws:security_group:vpc-0:rds-instance-1-security_group
+    tag: parent
 
   lambda_function/lambda_function_3:
-    children: aws:ecr_image:lambda_function_3-image,aws:iam_role:lambda_function_3-ExecutionRole,aws:log_group:lambda_function_3-log-group
+    children:
+        - aws:ecr_image:lambda_function_3-image
+        - aws:iam_role:lambda_function_3-ExecutionRole
+        - aws:security_group:vpc-0:lambda_function_3-security_group
     parent: vpc/vpc-0
-
+    tag: big
 
   lambda_function/lambda_function_0:
-    children: aws:ecr_image:lambda_function_0-image,aws:iam_role:lambda_function_0-ExecutionRole,aws:log_group:lambda_function_0-log-group
+    children:
+        - aws:ecr_image:lambda_function_0-image
+        - aws:ecr_repo:ecr_repo-0
+        - aws:iam_role:lambda_function_0-ExecutionRole
+        - aws:log_group:lambda_function_0-log-group
+        - aws:log_group:lambda_function_3-log-group
+        - aws:security_group:vpc-0:lambda_function_0-security_group
     parent: vpc/vpc-0
+    tag: big
 
   lambda_function/lambda_function_0 -> rds_instance/rds-instance-1:
-    path: aws:iam_role:lambda_function_0-ExecutionRole
-
-
diff --git a/pkg/engine2/testdata/remove_path.expect.yaml b/pkg/engine2/testdata/remove_path.expect.yaml
index 4c1f1ab5..9d4b30dc 100644
--- a/pkg/engine2/testdata/remove_path.expect.yaml
+++ b/pkg/engine2/testdata/remove_path.expect.yaml
@@ -113,8 +113,8 @@ resources:
                         - aws:rds_instance:rds-instance-1#RdsConnectionArn
                 Version: "2012-10-17"
         ManagedPolicies:
-            - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
             - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
+            - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
     aws:ecr_repo:ecr_repo-0:
         ForceDelete: true
     aws:elastic_ip:subnet-0-route_table-nat_gateway-elastic_ip:
@@ -215,6 +215,12 @@ resources:
               Protocol: "-1"
               ToPort: 0
         IngressRules:
+            - CidrBlocks:
+                - 10.0.128.0/18
+              Description: Allow ingress traffic from ip addresses within the subnet subnet-0
+              FromPort: 0
+              Protocol: "-1"
+              ToPort: 0
             - CidrBlocks:
                 - 10.0.192.0/18
               Description: Allow ingress traffic from ip addresses within the subnet subnet-1
@@ -226,12 +232,6 @@ resources:
               Protocol: "-1"
               Self: true
               ToPort: 0
-            - CidrBlocks:
-                - 10.0.128.0/18
-              Description: Allow ingress traffic from ip addresses within the subnet subnet-0
-              FromPort: 0
-              Protocol: "-1"
-              ToPort: 0
         Vpc: aws:vpc:vpc-0
     aws:route_table:vpc-0:subnet-0-route_table:
         Routes:
diff --git a/pkg/engine2/testdata/remove_path.iac-viz.yaml b/pkg/engine2/testdata/remove_path.iac-viz.yaml
index 23072b09..d21a37b4 100644
--- a/pkg/engine2/testdata/remove_path.iac-viz.yaml
+++ b/pkg/engine2/testdata/remove_path.iac-viz.yaml
@@ -5,71 +5,71 @@ resources:
   region/region-0:
 
   aws:internet_gateway:vpc-0/internet_gateway-0:
-  aws:internet_gateway:vpc-0/internet_gateway-0 -> vpc/vpc-0:
 
+  aws:internet_gateway:vpc-0/internet_gateway-0 -> vpc/vpc-0:
   aws:availability_zone:region-0/availability_zone-0:
-  aws:availability_zone:region-0/availability_zone-0 -> region/region-0:
 
+  aws:availability_zone:region-0/availability_zone-0 -> region/region-0:
   aws:route_table:vpc-0/subnet-2-route_table:
+
   aws:route_table:vpc-0/subnet-2-route_table -> aws:internet_gateway:vpc-0/internet_gateway-0:
   aws:route_table:vpc-0/subnet-2-route_table -> vpc/vpc-0:
-
   aws:availability_zone:region-0/availability_zone-1:
-  aws:availability_zone:region-0/availability_zone-1 -> region/region-0:
 
+  aws:availability_zone:region-0/availability_zone-1 -> region/region-0:
   aws:route_table:vpc-0/subnet-3-route_table:
+
   aws:route_table:vpc-0/subnet-3-route_table -> aws:internet_gateway:vpc-0/internet_gateway-0:
   aws:route_table:vpc-0/subnet-3-route_table -> vpc/vpc-0:
-
   elastic_ip/subnet-0-route_table-nat_gateway-elastic_ip:
 
   aws:subnet:vpc-0/subnet-2:
+
   aws:subnet:vpc-0/subnet-2 -> aws:availability_zone:region-0/availability_zone-0:
   aws:subnet:vpc-0/subnet-2 -> aws:route_table:vpc-0/subnet-2-route_table:
   aws:subnet:vpc-0/subnet-2 -> vpc/vpc-0:
-
   elastic_ip/subnet-1-route_table-nat_gateway-elastic_ip:
 
   aws:subnet:vpc-0/subnet-3:
+
   aws:subnet:vpc-0/subnet-3 -> aws:availability_zone:region-0/availability_zone-1:
   aws:subnet:vpc-0/subnet-3 -> aws:route_table:vpc-0/subnet-3-route_table:
   aws:subnet:vpc-0/subnet-3 -> vpc/vpc-0:
-
   aws:nat_gateway:subnet-2/subnet-0-route_table-nat_gateway:
+
   aws:nat_gateway:subnet-2/subnet-0-route_table-nat_gateway -> elastic_ip/subnet-0-route_table-nat_gateway-elastic_ip:
   aws:nat_gateway:subnet-2/subnet-0-route_table-nat_gateway -> aws:subnet:vpc-0/subnet-2:
-
   aws:nat_gateway:subnet-3/subnet-1-route_table-nat_gateway:
+
   aws:nat_gateway:subnet-3/subnet-1-route_table-nat_gateway -> elastic_ip/subnet-1-route_table-nat_gateway-elastic_ip:
   aws:nat_gateway:subnet-3/subnet-1-route_table-nat_gateway -> aws:subnet:vpc-0/subnet-3:
-
   aws:route_table:vpc-0/subnet-0-route_table:
+
   aws:route_table:vpc-0/subnet-0-route_table -> aws:nat_gateway:subnet-2/subnet-0-route_table-nat_gateway:
   aws:route_table:vpc-0/subnet-0-route_table -> vpc/vpc-0:
-
   aws:security_group:vpc-0/rds-instance-1-security_group:
-  aws:security_group:vpc-0/rds-instance-1-security_group -> vpc/vpc-0:
 
+  aws:security_group:vpc-0/rds-instance-1-security_group -> vpc/vpc-0:
   aws:route_table:vpc-0/subnet-1-route_table:
+
   aws:route_table:vpc-0/subnet-1-route_table -> aws:nat_gateway:subnet-3/subnet-1-route_table-nat_gateway:
   aws:route_table:vpc-0/subnet-1-route_table -> vpc/vpc-0:
-
   aws:subnet:vpc-0/subnet-0:
+
   aws:subnet:vpc-0/subnet-0 -> aws:availability_zone:region-0/availability_zone-0:
   aws:subnet:vpc-0/subnet-0 -> aws:route_table:vpc-0/subnet-0-route_table:
   aws:subnet:vpc-0/subnet-0 -> aws:security_group:vpc-0/rds-instance-1-security_group:
   aws:subnet:vpc-0/subnet-0 -> vpc/vpc-0:
-
   aws:subnet:vpc-0/subnet-1:
+
   aws:subnet:vpc-0/subnet-1 -> aws:availability_zone:region-0/availability_zone-1:
   aws:subnet:vpc-0/subnet-1 -> aws:route_table:vpc-0/subnet-1-route_table:
   aws:subnet:vpc-0/subnet-1 -> aws:security_group:vpc-0/rds-instance-1-security_group:
   aws:subnet:vpc-0/subnet-1 -> vpc/vpc-0:
-
   rds_subnet_group/rds_subnet_group-0:
+
   rds_subnet_group/rds_subnet_group-0 -> aws:subnet:vpc-0/subnet-0:
   rds_subnet_group/rds_subnet_group-0 -> aws:subnet:vpc-0/subnet-1:
-
   ecr_repo/ecr_repo-0:
 
   log_group/lambda_function_3-log-group:
@@ -77,45 +77,46 @@ resources:
   log_group/lambda_function_0-log-group:
 
   rds_instance/rds-instance-1:
+
   rds_instance/rds-instance-1 -> rds_subnet_group/rds_subnet_group-0:
   rds_instance/rds-instance-1 -> aws:security_group:vpc-0/rds-instance-1-security_group:
-
   ecr_image/lambda_function_3-image:
-  ecr_image/lambda_function_3-image -> ecr_repo/ecr_repo-0:
 
+  ecr_image/lambda_function_3-image -> ecr_repo/ecr_repo-0:
   iam_role/lambda_function_3-executionrole:
-  iam_role/lambda_function_3-executionrole -> log_group/lambda_function_3-log-group:
 
+  iam_role/lambda_function_3-executionrole -> log_group/lambda_function_3-log-group:
   aws:security_group:vpc-0/lambda_function_3-security_group:
-  aws:security_group:vpc-0/lambda_function_3-security_group -> vpc/vpc-0:
 
+  aws:security_group:vpc-0/lambda_function_3-security_group -> vpc/vpc-0:
   ecr_image/lambda_function_0-image:
-  ecr_image/lambda_function_0-image -> ecr_repo/ecr_repo-0:
 
+  ecr_image/lambda_function_0-image -> ecr_repo/ecr_repo-0:
   iam_role/lambda_function_0-executionrole:
+
   iam_role/lambda_function_0-executionrole -> log_group/lambda_function_0-log-group:
   iam_role/lambda_function_0-executionrole -> rds_instance/rds-instance-1:
-
   aws:security_group:vpc-0/lambda_function_0-security_group:
-  aws:security_group:vpc-0/lambda_function_0-security_group -> vpc/vpc-0:
 
+  aws:security_group:vpc-0/lambda_function_0-security_group -> vpc/vpc-0:
   route_table_association/subnet-3-subnet-3-route_table:
+
   route_table_association/subnet-3-subnet-3-route_table -> aws:route_table:vpc-0/subnet-3-route_table:
   route_table_association/subnet-3-subnet-3-route_table -> aws:subnet:vpc-0/subnet-3:
-
   route_table_association/subnet-2-subnet-2-route_table:
+
   route_table_association/subnet-2-subnet-2-route_table -> aws:route_table:vpc-0/subnet-2-route_table:
   route_table_association/subnet-2-subnet-2-route_table -> aws:subnet:vpc-0/subnet-2:
-
   route_table_association/subnet-1-subnet-1-route_table:
+
   route_table_association/subnet-1-subnet-1-route_table -> aws:route_table:vpc-0/subnet-1-route_table:
   route_table_association/subnet-1-subnet-1-route_table -> aws:subnet:vpc-0/subnet-1:
-
   route_table_association/subnet-0-subnet-0-route_table:
+
   route_table_association/subnet-0-subnet-0-route_table -> aws:route_table:vpc-0/subnet-0-route_table:
   route_table_association/subnet-0-subnet-0-route_table -> aws:subnet:vpc-0/subnet-0:
-
   lambda_function/lambda_function_3:
+
   lambda_function/lambda_function_3 -> ecr_image/lambda_function_3-image:
   lambda_function/lambda_function_3 -> iam_role/lambda_function_3-executionrole:
   lambda_function/lambda_function_3 -> log_group/lambda_function_3-log-group:
@@ -123,8 +124,8 @@ resources:
   lambda_function/lambda_function_3 -> aws:security_group:vpc-0/lambda_function_3-security_group:
   lambda_function/lambda_function_3 -> aws:subnet:vpc-0/subnet-0:
   lambda_function/lambda_function_3 -> aws:subnet:vpc-0/subnet-1:
-
   lambda_function/lambda_function_0:
+
   lambda_function/lambda_function_0 -> ecr_image/lambda_function_0-image:
   lambda_function/lambda_function_0 -> iam_role/lambda_function_0-executionrole:
   lambda_function/lambda_function_0 -> log_group/lambda_function_0-log-group:
@@ -132,4 +133,3 @@ resources:
   lambda_function/lambda_function_0 -> aws:security_group:vpc-0/lambda_function_0-security_group:
   lambda_function/lambda_function_0 -> aws:subnet:vpc-0/subnet-0:
   lambda_function/lambda_function_0 -> aws:subnet:vpc-0/subnet-1:
-
diff --git a/pkg/engine2/testdata/rename.dataflow-viz.yaml b/pkg/engine2/testdata/rename.dataflow-viz.yaml
index 2af05c54..8342b915 100644
--- a/pkg/engine2/testdata/rename.dataflow-viz.yaml
+++ b/pkg/engine2/testdata/rename.dataflow-viz.yaml
@@ -1,11 +1,16 @@
 provider: aws
 resources:
   dynamodb_table/dynamodb_table_1:
+    children:
+        - aws:iam_role:lambda_function_0-ExecutionRole
+        - aws:log_group:lambda_function_0-log-group
+        - aws:log_group:new_lambda_name-log-group
+    tag: big
 
   lambda_function/new_lambda_name:
-    children: aws:ecr_image:lambda_function_0-image,aws:iam_role:lambda_function_0-ExecutionRole,aws:log_group:new_lambda_name-log-group
+    children:
+        - aws:ecr_image:lambda_function_0-image
+        - aws:ecr_repo:ecr_repo-0
+    tag: big
 
   lambda_function/new_lambda_name -> dynamodb_table/dynamodb_table_1:
-    path: aws:SERVICE_API:lambda_function_0-lambda_function_0-log-group
-
-
diff --git a/pkg/engine2/testdata/rename.iac-viz.yaml b/pkg/engine2/testdata/rename.iac-viz.yaml
index d11a68dc..18970020 100644
--- a/pkg/engine2/testdata/rename.iac-viz.yaml
+++ b/pkg/engine2/testdata/rename.iac-viz.yaml
@@ -9,15 +9,15 @@ resources:
   log_group/new_lambda_name-log-group:
 
   ecr_image/lambda_function_0-image:
-  ecr_image/lambda_function_0-image -> ecr_repo/ecr_repo-0:
 
+  ecr_image/lambda_function_0-image -> ecr_repo/ecr_repo-0:
   iam_role/lambda_function_0-executionrole:
+
   iam_role/lambda_function_0-executionrole -> dynamodb_table/dynamodb_table_1:
   iam_role/lambda_function_0-executionrole -> log_group/lambda_function_0-log-group:
   iam_role/lambda_function_0-executionrole -> log_group/new_lambda_name-log-group:
-
   lambda_function/new_lambda_name:
+
   lambda_function/new_lambda_name -> dynamodb_table/dynamodb_table_1:
   lambda_function/new_lambda_name -> ecr_image/lambda_function_0-image:
   lambda_function/new_lambda_name -> iam_role/lambda_function_0-executionrole:
-
diff --git a/pkg/engine2/testdata/single_lambda.dataflow-viz.yaml b/pkg/engine2/testdata/single_lambda.dataflow-viz.yaml
index 45556b94..ae0fd618 100755
--- a/pkg/engine2/testdata/single_lambda.dataflow-viz.yaml
+++ b/pkg/engine2/testdata/single_lambda.dataflow-viz.yaml
@@ -1,6 +1,10 @@
 provider: aws
 resources:
   lambda_function/lambda_function_0:
-    children: aws:ecr_image:lambda_function_0-image,aws:iam_role:lambda_function_0-ExecutionRole,aws:log_group:lambda_function_0-log-group
-
+    children:
+        - aws:ecr_image:lambda_function_0-image
+        - aws:ecr_repo:ecr_repo-0
+        - aws:iam_role:lambda_function_0-ExecutionRole
+        - aws:log_group:lambda_function_0-log-group
+    tag: big
 
diff --git a/pkg/engine2/testdata/single_lambda.iac-viz.yaml b/pkg/engine2/testdata/single_lambda.iac-viz.yaml
index f40b22d4..777d6f5f 100755
--- a/pkg/engine2/testdata/single_lambda.iac-viz.yaml
+++ b/pkg/engine2/testdata/single_lambda.iac-viz.yaml
@@ -5,12 +5,12 @@ resources:
   log_group/lambda_function_0-log-group:
 
   ecr_image/lambda_function_0-image:
-  ecr_image/lambda_function_0-image -> ecr_repo/ecr_repo-0:
 
+  ecr_image/lambda_function_0-image -> ecr_repo/ecr_repo-0:
   iam_role/lambda_function_0-executionrole:
-  iam_role/lambda_function_0-executionrole -> log_group/lambda_function_0-log-group:
 
+  iam_role/lambda_function_0-executionrole -> log_group/lambda_function_0-log-group:
   lambda_function/lambda_function_0:
+
   lambda_function/lambda_function_0 -> ecr_image/lambda_function_0-image:
   lambda_function/lambda_function_0 -> iam_role/lambda_function_0-executionrole:
-
diff --git a/pkg/engine2/testdata/static_site.dataflow-viz.yaml b/pkg/engine2/testdata/static_site.dataflow-viz.yaml
index 96bdb413..45f661cf 100755
--- a/pkg/engine2/testdata/static_site.dataflow-viz.yaml
+++ b/pkg/engine2/testdata/static_site.dataflow-viz.yaml
@@ -1,9 +1,13 @@
 provider: aws
 resources:
   s3_bucket/s3-bucket-0:
+    children:
+        - aws:s3_bucket_policy:s3_bucket_policy-0
+    tag: big
 
   cloudfront_distribution/cloudfront_distribution_1:
-  cloudfront_distribution/cloudfront_distribution_1 -> s3_bucket/s3-bucket-0:
-    {}
-
+    children:
+        - aws:s3_bucket_policy:s3_bucket_policy-0
+    tag: big
 
+  cloudfront_distribution/cloudfront_distribution_1 -> s3_bucket/s3-bucket-0:
diff --git a/pkg/engine2/testdata/static_site.iac-viz.yaml b/pkg/engine2/testdata/static_site.iac-viz.yaml
index 3d31f00c..69b908fc 100755
--- a/pkg/engine2/testdata/static_site.iac-viz.yaml
+++ b/pkg/engine2/testdata/static_site.iac-viz.yaml
@@ -5,10 +5,10 @@ resources:
   s3_bucket/s3-bucket-0:
 
   s3_bucket_policy/s3_bucket_policy-0:
+
   s3_bucket_policy/s3_bucket_policy-0 -> cloudfront_origin_access_identity/cloudfront_origin_access_identity-0:
   s3_bucket_policy/s3_bucket_policy-0 -> s3_bucket/s3-bucket-0:
-
   cloudfront_distribution/cloudfront_distribution_1:
+
   cloudfront_distribution/cloudfront_distribution_1 -> cloudfront_origin_access_identity/cloudfront_origin_access_identity-0:
   cloudfront_distribution/cloudfront_distribution_1 -> s3_bucket/s3-bucket-0:
-