Repository: klothoplatform/klotho
Pull request #838 (2023-12-20 18:41:19)

fix eks bugs
fixes kubeconfig circular iac dependency and emitter bug to where we try to sanitize to the wrong type since its done before convert

### Standard checks

- **Unit tests**: Any special considerations?
- **Docs**: Do we need to update any docs, internal or public?
- **Backwards compatibility**: Will this break existing apps? If so, what would be the extra work required to keep them working?



Patch:
diff --git a/pkg/engine2/constraints.go b/pkg/engine2/constraints.go
index bb964360..30dd3042 100644
--- a/pkg/engine2/constraints.go
+++ b/pkg/engine2/constraints.go
@@ -4,7 +4,6 @@ import (
 	"errors"
 	"fmt"
 
-	"github.com/dominikbraun/graph"
 	construct "github.com/klothoplatform/klotho/pkg/construct2"
 	"github.com/klothoplatform/klotho/pkg/engine2/constraints"
 	"github.com/klothoplatform/klotho/pkg/engine2/reconciler"
@@ -120,46 +119,12 @@ func applyEdgeConstraint(ctx solution_context.SolutionContext, constraint constr
 		}
 	}
 
-	removePath := func() error {
-		paths, err := graph.AllPathsBetween(ctx.DataflowGraph(), constraint.Target.Source, constraint.Target.Target)
-		switch {
-		case errors.Is(err, graph.ErrTargetNotReachable):
-			return nil
-		case err != nil:
-			return err
-		}
-
-		var errs error
-
-		// first we will remove all dependencies that make up the paths from the constraints source to target
-		for _, path := range paths {
-			for i, res := range path {
-				if i == 0 {
-					continue
-				}
-				errs = errors.Join(errs, ctx.OperationalView().RemoveEdge(path[i-1], res))
-			}
-		}
-		if errs != nil {
-			return errs
-		}
-
-		// Next we will try to delete any node in those paths in case they no longer are required for the architecture
-		// We will pass the explicit field as false so that explicitly added resources do not get deleted
-		for _, path := range paths {
-			for _, resource := range path {
-				errs = errors.Join(errs, reconciler.RemoveResource(ctx, resource, false))
-			}
-		}
-		return errs
-	}
-
 	switch constraint.Operator {
 	case constraints.MustExistConstraintOperator:
 		return ctx.OperationalView().AddEdge(constraint.Target.Source, constraint.Target.Target)
 
 	case constraints.MustNotExistConstraintOperator:
-		return removePath()
+		return reconciler.RemovePath(constraint.Target.Source, constraint.Target.Target, ctx)
 	}
 	return nil
 }
diff --git a/pkg/engine2/operational_eval/dot.go b/pkg/engine2/operational_eval/dot.go
index fd5e7f8a..ed998000 100644
--- a/pkg/engine2/operational_eval/dot.go
+++ b/pkg/engine2/operational_eval/dot.go
@@ -39,10 +39,10 @@ func keyAttributes(eval *Evaluator, key Key) map[string]string {
 		if key.PathSatisfication.Classification != "" {
 			extra = append(extra, fmt.Sprintf("<%s>", key.PathSatisfication.Classification))
 		}
-		if propertyReferenceChangesBoundary(key.PathSatisfication.Target) {
+		if key.PathSatisfication.Target.PropertyReferenceChangesBoundary() {
 			extra = append(extra, fmt.Sprintf("target#%s", key.PathSatisfication.Target.PropertyReference))
 		}
-		if propertyReferenceChangesBoundary(key.PathSatisfication.Source) {
+		if key.PathSatisfication.Source.PropertyReferenceChangesBoundary() {
 			extra = append(extra, fmt.Sprintf("source#%s", key.PathSatisfication.Target.PropertyReference))
 		}
 		if len(extra) > 0 {
diff --git a/pkg/engine2/operational_eval/evaluator.go b/pkg/engine2/operational_eval/evaluator.go
index 8804ccae..f84e0ea9 100644
--- a/pkg/engine2/operational_eval/evaluator.go
+++ b/pkg/engine2/operational_eval/evaluator.go
@@ -125,10 +125,10 @@ func (key Key) String() string {
 		if key.PathSatisfication.Classification != "" {
 			args = append(args, fmt.Sprintf("<%s>", key.PathSatisfication.Classification))
 		}
-		if propertyReferenceChangesBoundary(key.PathSatisfication.Target) {
+		if key.PathSatisfication.Target.PropertyReferenceChangesBoundary() {
 			args = append(args, fmt.Sprintf("target#%s", key.PathSatisfication.Target.PropertyReference))
 		}
-		if propertyReferenceChangesBoundary(key.PathSatisfication.Source) {
+		if key.PathSatisfication.Source.PropertyReferenceChangesBoundary() {
 			args = append(args, fmt.Sprintf("source#%s", key.PathSatisfication.Target.PropertyReference))
 		}
 		return fmt.Sprintf("Expand(%s)", strings.Join(args, ", "))
diff --git a/pkg/engine2/operational_eval/graph.go b/pkg/engine2/operational_eval/graph.go
index 87e24d0d..66798ef5 100644
--- a/pkg/engine2/operational_eval/graph.go
+++ b/pkg/engine2/operational_eval/graph.go
@@ -94,7 +94,7 @@ func (eval *Evaluator) pathVertices(source, target construct.ResourceId) (graphC
 		// We are checking to see if either of the source or target nodes will change due to property references,
 		// if there are property references we want to ensure the correct dependency ordering is in place so
 		// we cannot yet split the expansion vertex up or build the temp graph
-		if propertyReferenceChangesBoundary(satisfication.Source) || propertyReferenceChangesBoundary(satisfication.Target) {
+		if satisfication.Source.PropertyReferenceChangesBoundary() || satisfication.Target.PropertyReferenceChangesBoundary() {
 			buildTempGraph = false
 		}
 
diff --git a/pkg/engine2/operational_eval/vertex_path_expand.go b/pkg/engine2/operational_eval/vertex_path_expand.go
index 545704a2..4a935642 100644
--- a/pkg/engine2/operational_eval/vertex_path_expand.go
+++ b/pkg/engine2/operational_eval/vertex_path_expand.go
@@ -310,7 +310,7 @@ func (v *pathExpandVertex) getExpansionsToRun(eval *Evaluator) ([]path_selection
 		return nil, fmt.Errorf("could not find target resource %s: %w", v.Edge.Target, err)
 	}
 	edge := construct.ResourceEdge{Source: sourceRes, Target: targetRes}
-	expansions, err := DeterminePathSatisfactionInputs(eval.Solution, v.Satisfication, edge)
+	expansions, err := path_selection.DeterminePathSatisfactionInputs(eval.Solution, v.Satisfication, edge)
 	if err != nil {
 		errs = errors.Join(errs, err)
 	}
@@ -510,68 +510,6 @@ func (v *pathExpandVertex) Dependencies(eval *Evaluator) (graphChanges, error) {
 	return changes, errs
 }
 
-func DeterminePathSatisfactionInputs(
-	sol solution_context.SolutionContext,
-	satisfaction knowledgebase.EdgePathSatisfaction,
-	edge construct.ResourceEdge,
-) (expansions []path_selection.ExpansionInput, errs error) {
-	srcIds := []construct.ResourceId{edge.Source.ID}
-	targetIds := []construct.ResourceId{edge.Target.ID}
-	var err error
-	if propertyReferenceChangesBoundary(satisfaction.Source) {
-		srcIds, err = solution_context.GetResourcesFromPropertyReference(sol, edge.Source.ID, satisfaction.Source.PropertyReference)
-		if err != nil {
-			errs = errors.Join(errs, fmt.Errorf(
-				"failed to determine path satisfaction inputs. could not find resource %s: %w",
-				edge.Source.ID, err,
-			))
-		}
-	}
-	if propertyReferenceChangesBoundary(satisfaction.Target) {
-		targetIds, err = solution_context.GetResourcesFromPropertyReference(sol, edge.Target.ID, satisfaction.Target.PropertyReference)
-		if err != nil {
-			errs = errors.Join(errs, fmt.Errorf(
-				"failed to determine path satisfaction inputs. could not find resource %s: %w",
-				edge.Target.ID, err,
-			))
-
-		}
-	}
-
-	for _, srcId := range srcIds {
-		for _, targetId := range targetIds {
-			if srcId == targetId {
-				continue
-			}
-			src, err := sol.RawView().Vertex(srcId)
-			if err != nil {
-				errs = errors.Join(errs, fmt.Errorf(
-					"failed to determine path satisfaction inputs. could not find resource %s: %w",
-					srcId, err,
-				))
-				continue
-			}
-
-			target, err := sol.RawView().Vertex(targetId)
-			if err != nil {
-				errs = errors.Join(errs, fmt.Errorf(
-					"failed to determine path satisfaction inputs. could not find resource %s: %w",
-					targetId, err,
-				))
-				continue
-			}
-
-			e := construct.ResourceEdge{Source: src, Target: target}
-			exp := path_selection.ExpansionInput{
-				Dep:            e,
-				Classification: satisfaction.Classification,
-			}
-			expansions = append(expansions, exp)
-		}
-	}
-	return
-}
-
 // getDepsForPropertyRef takes a property reference and recurses down until the property is not filled in on the resource
 // When we reach resources with missing property references, we know they are the property vertex keys we must depend on
 func getDepsForPropertyRef(
@@ -605,12 +543,3 @@ func getDepsForPropertyRef(
 	}
 	return keys
 }
-
-// propertyReferenceChangesBoundary returns whether the [PathSatisfactionRoute] changes the boundary of the path
-// via satisfaction rules. Note: validity checks do not change the boundary, so those return false.
-func propertyReferenceChangesBoundary(v knowledgebase.PathSatisfactionRoute) bool {
-	if v.Validity != "" {
-		return false
-	}
-	return v.PropertyReference != ""
-}
diff --git a/pkg/engine2/path_selection/candidate_weight.go b/pkg/engine2/path_selection/candidate_weight.go
index 3357cc33..89b5dc01 100644
--- a/pkg/engine2/path_selection/candidate_weight.go
+++ b/pkg/engine2/path_selection/candidate_weight.go
@@ -3,10 +3,11 @@ package path_selection
 import (
 	"errors"
 
+	"github.com/dominikbraun/graph"
 	"github.com/klothoplatform/klotho/pkg/collectionutil"
 	construct "github.com/klothoplatform/klotho/pkg/construct2"
-	"github.com/klothoplatform/klotho/pkg/engine2/operational_rule"
 	"github.com/klothoplatform/klotho/pkg/engine2/solution_context"
+	"github.com/klothoplatform/klotho/pkg/graph_addons"
 	knowledgebase "github.com/klothoplatform/klotho/pkg/knowledge_base2"
 )
 
@@ -52,7 +53,7 @@ func determineCandidateWeight(
 		weight += 9
 	}
 
-	undirected, err := operational_rule.BuildUndirectedGraph(ctx)
+	undirected, err := BuildUndirectedGraph(ctx)
 	if err != nil {
 		errs = errors.Join(errs, err)
 		return
@@ -97,3 +98,32 @@ func determineCandidateWeight(
 	weight += availableWeight
 	return
 }
+
+func BuildUndirectedGraph(ctx solution_context.SolutionContext) (construct.Graph, error) {
+	undirected := graph.NewWithStore(
+		construct.ResourceHasher,
+		graph_addons.NewMemoryStore[construct.ResourceId, *construct.Resource](),
+	)
+	err := undirected.AddVerticesFrom(ctx.RawView())
+	if err != nil {
+		return nil, err
+	}
+	edges, err := ctx.RawView().Edges()
+	if err != nil {
+		return nil, err
+	}
+	for _, e := range edges {
+		weight := 1
+		// increase weights for edges that are connected to a functional resource
+		if knowledgebase.GetFunctionality(ctx.KnowledgeBase(), e.Source) != knowledgebase.Unknown {
+			weight = 1000
+		} else if knowledgebase.GetFunctionality(ctx.KnowledgeBase(), e.Target) != knowledgebase.Unknown {
+			weight = 1000
+		}
+		err := undirected.AddEdge(e.Source, e.Target, graph.EdgeWeight(weight))
+		if err != nil {
+			return nil, err
+		}
+	}
+	return undirected, nil
+}
diff --git a/pkg/engine2/path_selection/paths.go b/pkg/engine2/path_selection/paths.go
new file mode 100644
index 00000000..20596817
--- /dev/null
+++ b/pkg/engine2/path_selection/paths.go
@@ -0,0 +1,159 @@
+package path_selection
+
+import (
+	"errors"
+	"fmt"
+
+	"github.com/dominikbraun/graph"
+	construct "github.com/klothoplatform/klotho/pkg/construct2"
+	"github.com/klothoplatform/klotho/pkg/engine2/solution_context"
+	knowledgebase "github.com/klothoplatform/klotho/pkg/knowledge_base2"
+)
+
+func GetPaths(
+	sol solution_context.SolutionContext,
+	source, target construct.ResourceId,
+	pathValidityChecks func(source, target construct.ResourceId, path []construct.ResourceId) bool,
+	hasPathCheck bool,
+) ([]construct.Path, error) {
+	var errs error
+	var result []construct.Path
+	pathsCache := map[construct.SimpleEdge][][]construct.ResourceId{}
+	pathSatisfactions, err := sol.KnowledgeBase().GetPathSatisfactionsFromEdge(source, target)
+	if err != nil {
+		return result, err
+	}
+	sourceRes, err := sol.RawView().Vertex(source)
+	if err != nil {
+		return result, fmt.Errorf("has path could not find source resource %s: %w", source, err)
+	}
+	targetRes, err := sol.RawView().Vertex(target)
+	if err != nil {
+		return result, fmt.Errorf("has path could not find target resource %s: %w", target, err)
+	}
+	edge := construct.ResourceEdge{Source: sourceRes, Target: targetRes}
+	for _, satisfaction := range pathSatisfactions {
+		expansions, err := DeterminePathSatisfactionInputs(sol, satisfaction, edge)
+		if err != nil {
+			return result, err
+		}
+		for _, expansion := range expansions {
+			simple := construct.SimpleEdge{Source: expansion.Dep.Source.ID, Target: expansion.Dep.Target.ID}
+			paths, found := pathsCache[simple]
+			if !found {
+				var err error
+				paths, err = graph.AllPathsBetween(sol.RawView(), expansion.Dep.Source.ID, expansion.Dep.Target.ID)
+				if err != nil {
+					errs = errors.Join(errs, err)
+					continue
+				}
+				pathsCache[simple] = paths
+			}
+			if len(paths) == 0 {
+				return result, nil
+			}
+			// we have to track the result of each expansion because if we cant find a path for a single expansion
+			// we denote that we dont have an actual path from src -> target
+			var expansionResult []construct.ResourceId
+			if expansion.Classification != "" {
+			PATHS:
+				for _, path := range paths {
+					for i, res := range path {
+						if i == 0 {
+							continue
+						}
+						if et := sol.KnowledgeBase().GetEdgeTemplate(path[i-1], res); et != nil && et.DirectEdgeOnly {
+							continue PATHS
+						}
+
+					}
+					if !PathSatisfiesClassification(sol.KnowledgeBase(), path, expansion.Classification) {
+						continue PATHS
+					}
+					if !pathValidityChecks(source, target, path) {
+						continue PATHS
+					}
+					result = append(result, path)
+					expansionResult = path
+					if hasPathCheck {
+						break
+					}
+				}
+			} else {
+				expansionResult = paths[0]
+				for _, path := range paths {
+					result = append(result, path)
+				}
+				if hasPathCheck {
+					break
+				}
+			}
+			if expansionResult == nil {
+				return nil, nil
+			}
+		}
+	}
+	return result, nil
+}
+
+func DeterminePathSatisfactionInputs(
+	sol solution_context.SolutionContext,
+	satisfaction knowledgebase.EdgePathSatisfaction,
+	edge construct.ResourceEdge,
+) (expansions []ExpansionInput, errs error) {
+	srcIds := []construct.ResourceId{edge.Source.ID}
+	targetIds := []construct.ResourceId{edge.Target.ID}
+	var err error
+	if satisfaction.Source.PropertyReferenceChangesBoundary() {
+		srcIds, err = solution_context.GetResourcesFromPropertyReference(sol, edge.Source.ID, satisfaction.Source.PropertyReference)
+		if err != nil {
+			errs = errors.Join(errs, fmt.Errorf(
+				"failed to determine path satisfaction inputs. could not find resource %s: %w",
+				edge.Source.ID, err,
+			))
+		}
+	}
+	if satisfaction.Target.PropertyReferenceChangesBoundary() {
+		targetIds, err = solution_context.GetResourcesFromPropertyReference(sol, edge.Target.ID, satisfaction.Target.PropertyReference)
+		if err != nil {
+			errs = errors.Join(errs, fmt.Errorf(
+				"failed to determine path satisfaction inputs. could not find resource %s: %w",
+				edge.Target.ID, err,
+			))
+
+		}
+	}
+
+	for _, srcId := range srcIds {
+		for _, targetId := range targetIds {
+			if srcId == targetId {
+				continue
+			}
+			src, err := sol.RawView().Vertex(srcId)
+			if err != nil {
+				errs = errors.Join(errs, fmt.Errorf(
+					"failed to determine path satisfaction inputs. could not find resource %s: %w",
+					srcId, err,
+				))
+				continue
+			}
+
+			target, err := sol.RawView().Vertex(targetId)
+			if err != nil {
+				errs = errors.Join(errs, fmt.Errorf(
+					"failed to determine path satisfaction inputs. could not find resource %s: %w",
+					targetId, err,
+				))
+				continue
+			}
+
+			e := construct.ResourceEdge{Source: src, Target: target}
+			exp := ExpansionInput{
+				Dep:            e,
+				Classification: satisfaction.Classification,
+			}
+			expansions = append(expansions, exp)
+		}
+	}
+	return
+}
diff --git a/pkg/engine2/reconciler/remove_edge.go b/pkg/engine2/reconciler/remove_edge.go
new file mode 100644
index 00000000..1609fdb2
--- /dev/null
+++ b/pkg/engine2/reconciler/remove_edge.go
@@ -0,0 +1,210 @@
+package reconciler
+
+import (
+	"errors"
+	"fmt"
+
+	"github.com/dominikbraun/graph"
+	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/set"
+	"go.uber.org/zap"
+)
+
+// RemovePath removes all paths between the source and target node.
+//
+// It will determine when edges within those paths are used for contexts outside of the source and target paths and not remove them.
+func RemovePath(
+	source, target construct.ResourceId,
+	ctx solution_context.SolutionContext,
+) error {
+	zap.S().Infof("Removing path %s -> %s", source, target)
+	paths, err := graph.AllPathsBetween(ctx.DataflowGraph(), source, target)
+	switch {
+	case errors.Is(err, graph.ErrTargetNotReachable):
+		return nil
+	case err != nil:
+		return err
+	}
+
+	nodes := nodesInPaths(paths)
+	used, err := nodesUsedOutsideOfContext(nodes, ctx)
+	if err != nil {
+		return err
+	}
+	edges, err := findEdgesUsedInOtherPathSelection(source, target, nodes, ctx)
+	if err != nil {
+		return err
+	}
+
+	var errs error
+	for _, path := range paths {
+		errs = errors.Join(errs, removeSinglePath(source, target, path, used, edges, ctx))
+	}
+
+	// Next we will try to delete any node in those paths in case they no longer are required for the architecture
+	// We will pass the explicit field as false so that explicitly added resources do not get deleted
+	for _, path := range paths {
+		for _, resource := range path {
+			errs = errors.Join(errs, RemoveResource(ctx, resource, false))
+		}
+	}
+	return errs
+}
+
+// removeSinglePath removes all edges in a single path between the source and target node, if they are allowed to be removed.
+//
+// in order for an edge to be removed:
+// The source of the edge must not have other upstream dependencies (signaling there are other paths its connecting)
+// The edge must not be used in path solving another connection from the source or to the target
+func removeSinglePath(
+	source, target construct.ResourceId,
+	path []construct.ResourceId,
+	used set.Set[construct.ResourceId],
+	edges set.Set[construct.SimpleEdge],
+	ctx solution_context.SolutionContext,
+) error {
+	var errs error
+	// first we will remove all dependencies that make up the paths from the constraints source to target
+	for i, res := range path {
+		if i == 0 {
+			continue
+		}
+		// check if the previous resource is used outside of its context.
+		// Since we are deleting the edge downstream we have to make sure the source is not used,
+		// resulting in this edge being a part of another path
+		if used.Contains(path[i-1]) && !target.Matches(path[i-1]) && !source.Matches(path[i-1]) {
+			continue
+		}
+		if edges.Contains(construct.SimpleEdge{Source: path[i-1], Target: res}) {
+			continue
+		}
+		errs = errors.Join(errs, ctx.OperationalView().RemoveEdge(path[i-1], res))
+		if i > 1 {
+			errs = errors.Join(errs, RemoveResource(ctx, path[i-1], false))
+		}
+	}
+	return errs
+}
+
+func nodesInPaths(
+	paths [][]construct.ResourceId,
+) set.Set[construct.ResourceId] {
+	nodes := make(set.Set[construct.ResourceId])
+	for _, path := range paths {
+		for _, res := range path {
+			nodes.Add(res)
+		}
+	}
+	return nodes
+}
+
+// nodesUsedOutsideOfContext returns all nodes that are used outside of the context.
+// Being used outside of the context entails that there are upstream connections in the dataflow graph
+// outside of the set of nodes used in the all paths between the source and target node.
+//
+// We only care about upstream because any extra connections downstream will stay intact and wont result in other
+// paths being affected
+func nodesUsedOutsideOfContext(
+	nodes set.Set[construct.ResourceId],
+	ctx solution_context.SolutionContext,
+) (set.Set[construct.ResourceId], error) {
+	var errs error
+	used := make(set.Set[construct.ResourceId])
+	pred, err := ctx.RawView().PredecessorMap()
+	if err != nil {
+		return nil, err
+	}
+	for node := range nodes {
+		upstreams := pred[node]
+		for upstream := range upstreams {
+			if !nodes.Contains(upstream) {
+				used.Add(node)
+			}
+		}
+	}
+	return used, errs
+}
+
+// findEdgesUsedInOtherPathSelection returns all edges that are used in other path selections to the target or from the source.
+func findEdgesUsedInOtherPathSelection(
+	source, target construct.ResourceId,
+	nodes set.Set[construct.ResourceId],
+	ctx solution_context.SolutionContext,
+) (set.Set[construct.SimpleEdge], error) {
+	edges := make(set.Set[construct.SimpleEdge])
+	var errs error
+	upstreams, err := construct.AllUpstreamDependencies(ctx.DataflowGraph(), target)
+	if err != nil {
+		errs = errors.Join(errs, err)
+	}
+	for _, upstream := range upstreams {
+		if nodes.Contains(upstream) {
+			continue
+		}
+		upstreamRT, err := ctx.KnowledgeBase().GetResourceTemplate(upstream)
+		if err != nil {
+			errs = errors.Join(errs, err)
+			continue
+		} else if upstreamRT == nil {
+			errs = errors.Join(errs, fmt.Errorf("resource template %s not found", upstream))
+			continue
+		}
+		if len(upstreamRT.PathSatisfaction.AsSource) == 0 {
+			continue
+		}
+		paths, err := path_selection.GetPaths(ctx, upstream, target,
+			func(source, target construct.ResourceId, path []construct.ResourceId) bool { return true }, false)
+		if err != nil {
+			errs = errors.Join(errs, err)
+			continue
+		}
+		for _, path := range paths {
+			for i, res := range path {
+				if i == 0 {
+					continue
+				}
+				edges.Add(construct.SimpleEdge{Source: path[i-1], Target: res})
+			}
+		}
+	}
+	downstreams, err := construct.AllDownstreamDependencies(ctx.DataflowGraph(), source)
+	if err != nil {
+		errs = errors.Join(errs, err)
+	}
+	for _, downstream := range downstreams {
+		if nodes.Contains(downstream) {
+			continue
+		}
+		downstreamRT, err := ctx.KnowledgeBase().GetResourceTemplate(downstream)
+		if err != nil {
+			errs = errors.Join(errs, err)
+			continue
+		} else if downstreamRT == nil {
+			errs = errors.Join(errs, fmt.Errorf("resource template %s not found", downstream))
+			continue
+		}
+		if len(downstreamRT.PathSatisfaction.AsTarget) == 0 {
+			continue
+		}
+
+		paths, err := path_selection.GetPaths(ctx, source, downstream,
+			func(source, target construct.ResourceId, path []construct.ResourceId) bool { return true }, false)
+		if err != nil {
+			errs = errors.Join(errs, err)
+			continue
+		}
+
+		for _, path := range paths {
+			for i, res := range path {
+				if i == 0 {
+					continue
+				}
+				edges.Add(construct.SimpleEdge{Source: path[i-1], Target: res})
+
+			}
+		}
+	}
+	return edges, errs
+}
diff --git a/pkg/engine2/visualizer.go b/pkg/engine2/visualizer.go
index 2a98a840..6b077fe3 100644
--- a/pkg/engine2/visualizer.go
+++ b/pkg/engine2/visualizer.go
@@ -9,7 +9,6 @@ import (
 	"github.com/dominikbraun/graph"
 	"github.com/klothoplatform/klotho/pkg/collectionutil"
 	construct "github.com/klothoplatform/klotho/pkg/construct2"
-	"github.com/klothoplatform/klotho/pkg/engine2/operational_eval"
 	"github.com/klothoplatform/klotho/pkg/engine2/path_selection"
 	"github.com/klothoplatform/klotho/pkg/engine2/solution_context"
 	klotho_io "github.com/klothoplatform/klotho/pkg/io"
@@ -310,72 +309,16 @@ func HasPath(topo Topology, sol solution_context.SolutionContext, source, target
 		return false, nil
 	}
 	return checkPaths(topo, sol, source, target)
-
 }
 
 func checkPaths(topo Topology, sol solution_context.SolutionContext, source, target construct.ResourceId) (bool, error) {
-	var errs error
-	pathsCache := map[construct.SimpleEdge][][]construct.ResourceId{}
-	pathSatisfactions, err := sol.KnowledgeBase().GetPathSatisfactionsFromEdge(source, target)
-	if err != nil {
-		return false, err
-	}
-	sourceRes, err := sol.RawView().Vertex(source)
-	if err != nil {
-		return false, fmt.Errorf("has path could not find source resource %s: %w", source, err)
-	}
-	targetRes, err := sol.RawView().Vertex(target)
-	if err != nil {
-		return false, fmt.Errorf("has path could not find target resource %s: %w", target, err)
-	}
-	edge := construct.ResourceEdge{Source: sourceRes, Target: targetRes}
-	for _, satisfaction := range pathSatisfactions {
-		expansions, err := operational_eval.DeterminePathSatisfactionInputs(sol, satisfaction, edge)
-		if err != nil {
-			return false, err
-		}
-		for _, expansion := range expansions {
-			simple := construct.SimpleEdge{Source: expansion.Dep.Source.ID, Target: expansion.Dep.Target.ID}
-			paths, found := pathsCache[simple]
-			if !found {
-				var err error
-				paths, err = graph.AllPathsBetween(sol.RawView(), expansion.Dep.Source.ID, expansion.Dep.Target.ID)
-				if err != nil {
-					errs = errors.Join(errs, err)
-					continue
-				}
-				pathsCache[simple] = paths
-			}
-			if len(paths) == 0 {
-				return false, nil
-			}
-			containedClassification := false
-			if expansion.Classification != "" {
-			PATHS:
-				for _, path := range paths {
-					for i, res := range path {
-						if i == 0 {
-							continue
-						}
-						if et := sol.KnowledgeBase().GetEdgeTemplate(path[i-1], res); et != nil && et.DirectEdgeOnly {
-							continue PATHS
-						}
-						if i < len(path)-1 && (topo.Nodes[res.String()] != nil && res != source && res != target) {
-							continue PATHS
-						}
-					}
-					if path_selection.PathSatisfiesClassification(sol.KnowledgeBase(), path, expansion.Classification) {
-						containedClassification = true
-						break
-					}
-				}
-			} else {
-				containedClassification = true
-			}
-			if !containedClassification {
-				return false, nil
+	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
 			}
 		}
-	}
-	return true, nil
+		return true
+	}, true)
+	return len(paths) > 0, err
 }
diff --git a/pkg/infra/iac3/plugin.go b/pkg/infra/iac3/plugin.go
index b438347e..b249039a 100644
--- a/pkg/infra/iac3/plugin.go
+++ b/pkg/infra/iac3/plugin.go
@@ -145,7 +145,7 @@ func (p Plugin) Translate(ctx solution_context.SolutionContext) ([]kio.File, err
 func (p *Plugin) sanitizeConfig() error {
 	reg, err := regexp.Compile("[^a-zA-Z0-9-_]+")
 	if err != nil {
-		return fmt.Errorf("Error compiling regex: %v", err)
+		return fmt.Errorf("error compiling regex: %v", err)
 	}
 	p.Config.AppName = reg.ReplaceAllString(p.Config.AppName, "")
 	return nil
diff --git a/pkg/knowledge_base2/emitter.go b/pkg/knowledge_base2/emitter.go
index 21163300..581ae7a4 100644
--- a/pkg/knowledge_base2/emitter.go
+++ b/pkg/knowledge_base2/emitter.go
@@ -90,24 +90,24 @@ func ConsumeFromResource(consumer, emitter *construct.Resource, ctx DynamicConte
 					addErr(consume, emit, err)
 					continue
 				}
-				val, err = sanitizeForConsumption(ctx, resource, consumeTmpl.GetProperty(consume.PropertyPath), val)
+				pval, err := resource.GetProperty(consume.PropertyPath)
 				if err != nil {
 					addErr(consume, emit, err)
 					continue
 				}
-				pval, err := resource.GetProperty(consume.PropertyPath)
+				if consume.Converter != "" {
+					val, err = consume.Convert(val, id, ctx)
+					if err != nil {
+						addErr(consume, emit, err)
+						continue
+					}
+				}
+				val, err = sanitizeForConsumption(ctx, resource, consumeTmpl.GetProperty(consume.PropertyPath), val)
 				if err != nil {
 					addErr(consume, emit, err)
 					continue
 				}
 				if pval == nil {
-					if consume.Converter != "" {
-						val, err = consume.Convert(val, id, ctx)
-						if err != nil {
-							addErr(consume, emit, err)
-							continue
-						}
-					}
 					delays = append(delays, DelayedConsumption{
 						Value:        val,
 						Resource:     id,
@@ -115,7 +115,6 @@ func ConsumeFromResource(consumer, emitter *construct.Resource, ctx DynamicConte
 					})
 					continue
 				}
-
 				err = consume.Consume(val, ctx, resource)
 				if err != nil {
 					addErr(consume, emit, err)
diff --git a/pkg/knowledge_base2/path_satisfaction.go b/pkg/knowledge_base2/path_satisfaction.go
index 25129d35..8fa38e7e 100644
--- a/pkg/knowledge_base2/path_satisfaction.go
+++ b/pkg/knowledge_base2/path_satisfaction.go
@@ -94,3 +94,10 @@ func (kb *KnowledgeBase) GetPathSatisfactionsFromEdge(source, target construct.R
 	}
 	return pathSatisfications, nil
 }
+
+func (v PathSatisfactionRoute) PropertyReferenceChangesBoundary() bool {
+	if v.Validity != "" {
+		return false
+	}
+	return v.PropertyReference != ""
+}
diff --git a/pkg/knowledge_base2/properties/list_property.go b/pkg/knowledge_base2/properties/list_property.go
index 40c045c1..d56e9ac6 100644
--- a/pkg/knowledge_base2/properties/list_property.go
+++ b/pkg/knowledge_base2/properties/list_property.go
@@ -177,7 +177,7 @@ func (l *ListProperty) Validate(resource *construct.Resource, value any, ctx kno
 
 	listVal, ok := value.([]any)
 	if !ok {
-		return fmt.Errorf("invalid map value %v", value)
+		return fmt.Errorf("invalid list value %v", value)
 	}
 	if l.MinLength != nil {
 		if len(listVal) < *l.MinLength {
diff --git a/pkg/templates/aws/edges/eks_cluster-vpc.yaml b/pkg/templates/aws/edges/eks_cluster-vpc.yaml
index 153e504c..a0d58294 100644
--- a/pkg/templates/aws/edges/eks_cluster-vpc.yaml
+++ b/pkg/templates/aws/edges/eks_cluster-vpc.yaml
@@ -25,4 +25,9 @@ operational_rules:
           - selector: aws:security_group_rule
             properties:
               SecurityGroupId: '{{ fieldRef "ClusterSecurityGroup" .Source }}'
-              Type: ingress
\ No newline at end of file
+              Type: ingress
+      - resource: '{{ .Source }}' # Temporarily generate the kube_config for the cluster
+        direction: upstream
+        resources:
+          - kubernetes:kube_config
+        unique: true
diff --git a/pkg/templates/aws/resources/eks_cluster.yaml b/pkg/templates/aws/resources/eks_cluster.yaml
index faa95806..227e4f55 100644
--- a/pkg/templates/aws/resources/eks_cluster.yaml
+++ b/pkg/templates/aws/resources/eks_cluster.yaml
@@ -39,16 +39,6 @@ properties:
           - aws:security_group
         unique: true
     description: Lists the security groups associated with the EKS cluster nodes
-  KubeConfig:
-    type: resource(kubernetes:kube_config)
-    operational_rule:
-      step:
-        direction: upstream
-        resources:
-          - kubernetes:kube_config
-        unique: true
-    description: Points to a Kubernetes kubeconfig file containing cluster access
-      information
   Name:
     type: string
     configuration_disabled: true
diff --git a/pkg/templates/aws/resources/elasticache_subnet_group.yaml b/pkg/templates/aws/resources/elasticache_subnet_group.yaml
index 596c32f7..d17203ea 100644
--- a/pkg/templates/aws/resources/elasticache_subnet_group.yaml
+++ b/pkg/templates/aws/resources/elasticache_subnet_group.yaml
@@ -1,6 +1,19 @@
 qualified_type_name: aws:elasticache_subnet_group
 display_name: ElastiCache Subnet Group
-
+sanitize_name:
+  # Identifiers have these naming constraints:
+  # - Must contain 1–63 alphanumeric characters or hyphens.
+  # - First character must be a letter.
+  # - Can't end with a hyphen or contain two consecutive hyphens.
+  |
+  {{ . 
+    | replace `^[^[:alpha:]]+` "" 
+    | replace `--+` "-" 
+    | replace `-$` ""
+    | replace `[^[:alnum:]-]+` "-"
+    | length 1 63
+  }}
+  
 properties:
   Subnets:
     type: list(resource(aws:subnet))


Test patch:
diff --git a/pkg/engine2/reconciler/remove_edge_test.go b/pkg/engine2/reconciler/remove_edge_test.go
new file mode 100644
index 00000000..25de518a
--- /dev/null
+++ b/pkg/engine2/reconciler/remove_edge_test.go
@@ -0,0 +1,162 @@
+package reconciler
+
+import (
+	"testing"
+
+	"github.com/dominikbraun/graph"
+	construct "github.com/klothoplatform/klotho/pkg/construct2"
+	"github.com/klothoplatform/klotho/pkg/construct2/graphtest"
+	"github.com/klothoplatform/klotho/pkg/engine2/enginetesting"
+	knowledgebase "github.com/klothoplatform/klotho/pkg/knowledge_base2"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+)
+
+func Test_RemovePath(t *testing.T) {
+	tests := []struct {
+		name         string
+		source       construct.ResourceId
+		target       construct.ResourceId
+		mockKB       []mock.Call
+		initialState []any
+		want         []any
+	}{
+		{
+			name:   "remove path simple",
+			source: construct.ResourceId{Provider: "p", Type: "t", Name: "s"},
+			target: construct.ResourceId{Provider: "p", Type: "t", Name: "t"},
+			mockKB: []mock.Call{
+				{
+					Method:    "GetResourceTemplate",
+					Arguments: []any{mock.Anything},
+					ReturnArguments: []any{&knowledgebase.ResourceTemplate{
+						Classification: knowledgebase.Classification{Is: []string{string(knowledgebase.Compute)}},
+					}, nil},
+				},
+				{
+					Method:          "GetEdgeTemplate",
+					Arguments:       []any{mock.Anything, mock.Anything},
+					ReturnArguments: []any{&knowledgebase.EdgeTemplate{}},
+				},
+			},
+			initialState: []any{"p:t:s", "p:t:t", "p:t:s -> p:t:t"},
+			want:         []any{"p:t:s", "p:t:t"},
+		},
+		{
+			name:   "remove shared path",
+			source: construct.ResourceId{Provider: "p", Type: "t", Name: "s"},
+			target: construct.ResourceId{Provider: "p", Type: "t", Name: "b"},
+			mockKB: []mock.Call{
+				{
+					Method:    "GetResourceTemplate",
+					Arguments: []any{mock.Anything},
+					ReturnArguments: []any{&knowledgebase.ResourceTemplate{
+						Classification: knowledgebase.Classification{Is: []string{string(knowledgebase.Compute)}},
+					}, nil},
+				},
+				{
+					Method:          "GetEdgeTemplate",
+					Arguments:       []any{mock.Anything, mock.Anything},
+					ReturnArguments: []any{&knowledgebase.EdgeTemplate{}},
+				},
+				{
+					Method:    "GetPathSatisfactionsFromEdge",
+					Arguments: []any{mock.Anything, mock.Anything},
+					ReturnArguments: []any{
+						[]knowledgebase.EdgePathSatisfaction{}, nil,
+					},
+				},
+			},
+			initialState: []any{"p:t:s", "p:t:t", "p:t:s -> p:t:t", "p:t:a", "p:t:b", "p:t:a -> p:t:t", "p:t:t -> p:t:b"},
+			want:         []any{"p:t:s", "p:t:t", "p:t:a", "p:t:b", "p:t:a -> p:t:t", "p:t:t -> p:t:b"},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			testSolution := enginetesting.NewTestSolution()
+			for _, call := range tt.mockKB {
+				testSolution.KB.On(call.Method, call.Arguments...).Return(call.ReturnArguments...)
+			}
+			testSolution.LoadState(t, tt.initialState...)
+			err := RemovePath(tt.source, tt.target, testSolution)
+			if !assert.NoError(err) {
+				return
+			}
+			expect := graphtest.MakeGraph(t, construct.NewGraph(), tt.want...)
+			graphtest.AssertGraphEqual(t, expect, testSolution.DataflowGraph(), "graph not expected")
+		})
+	}
+}
+
+func Test_findEdgesUsedInOtherPathSelection(t *testing.T) {
+	tests := []struct {
+		name         string
+		source       construct.ResourceId
+		target       construct.ResourceId
+		mockKB       []mock.Call
+		initialState []any
+		want         []construct.SimpleEdge
+	}{
+		{
+			name:   "find shared edges",
+			source: construct.ResourceId{Provider: "p", Type: "t", Name: "s"},
+			target: construct.ResourceId{Provider: "p", Type: "t", Name: "b"},
+			mockKB: []mock.Call{
+				{
+					Method:    "GetResourceTemplate",
+					Arguments: []any{mock.Anything},
+					ReturnArguments: []any{&knowledgebase.ResourceTemplate{
+						Classification: knowledgebase.Classification{Is: []string{string(knowledgebase.Compute)}},
+						PathSatisfaction: knowledgebase.PathSatisfaction{
+							AsSource: []knowledgebase.PathSatisfactionRoute{
+								{Classification: ""},
+							},
+							AsTarget: []knowledgebase.PathSatisfactionRoute{
+								{Classification: ""},
+							},
+						},
+					}, nil},
+				},
+				{
+					Method:          "GetEdgeTemplate",
+					Arguments:       []any{mock.Anything, mock.Anything},
+					ReturnArguments: []any{&knowledgebase.EdgeTemplate{}},
+				},
+				{
+					Method:    "GetPathSatisfactionsFromEdge",
+					Arguments: []any{mock.Anything, mock.Anything},
+					ReturnArguments: []any{
+						[]knowledgebase.EdgePathSatisfaction{
+							{Classification: ""},
+						}, nil,
+					},
+				},
+			},
+			initialState: []any{"p:t:s", "p:t:t", "p:t:s -> p:t:t", "p:t:a", "p:t:b", "p:t:a -> p:t:t", "p:t:t -> p:t:b"},
+			want: []construct.SimpleEdge{
+				{Source: construct.ResourceId{Provider: "p", Type: "t", Name: "a"}, Target: construct.ResourceId{Provider: "p", Type: "t", Name: "t"}},
+				{Source: construct.ResourceId{Provider: "p", Type: "t", Name: "t"}, Target: construct.ResourceId{Provider: "p", Type: "t", Name: "b"}},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			testSolution := enginetesting.NewTestSolution()
+			for _, call := range tt.mockKB {
+				testSolution.KB.On(call.Method, call.Arguments...).Return(call.ReturnArguments...)
+			}
+			testSolution.LoadState(t, tt.initialState...)
+			paths, err := graph.AllPathsBetween(testSolution.DataflowGraph(), tt.source, tt.target)
+			if !assert.NoError(err) {
+				return
+			}
+			edges, err := findEdgesUsedInOtherPathSelection(tt.source, tt.target, nodesInPaths(paths), testSolution)
+			if !assert.NoError(err) {
+				return
+			}
+			assert.ElementsMatch(tt.want, edges.ToSlice())
+		})
+	}
+}
diff --git a/pkg/engine2/testdata/k8s_api.expect.yaml b/pkg/engine2/testdata/k8s_api.expect.yaml
index eb4a2c7a..224983cf 100755
--- a/pkg/engine2/testdata/k8s_api.expect.yaml
+++ b/pkg/engine2/testdata/k8s_api.expect.yaml
@@ -452,7 +452,6 @@ resources:
         Region: aws:region:region-0
     aws:eks_cluster:eks_cluster-0:
         ClusterRole: aws:iam_role:ClusterRole-eks_cluster-0
-        KubeConfig: kubernetes:kube_config:eks_cluster-0-kube_config
         SecurityGroups:
             - aws:security_group:vpc-0:eks_cluster-0-security_group
         Subnets:
diff --git a/pkg/engine2/testdata/remove_path.dataflow-viz.yaml b/pkg/engine2/testdata/remove_path.dataflow-viz.yaml
new file mode 100644
index 00000000..e0356003
--- /dev/null
+++ b/pkg/engine2/testdata/remove_path.dataflow-viz.yaml
@@ -0,0 +1,22 @@
+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
+    parent: vpc/vpc-0
+
+
+  vpc/vpc-0:
+
+  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
+    parent: vpc/vpc-0
+
+
+  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
+    parent: vpc/vpc-0
+
+  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
new file mode 100644
index 00000000..4c1f1ab5
--- /dev/null
+++ b/pkg/engine2/testdata/remove_path.expect.yaml
@@ -0,0 +1,311 @@
+resources:
+    aws:security_group:vpc-0:lambda_function_0-security_group:
+        EgressRules:
+            - CidrBlocks:
+                - 0.0.0.0/0
+              Description: Allows all outbound IPv4 traffic
+              FromPort: 0
+              Protocol: "-1"
+              ToPort: 0
+        IngressRules:
+            - Description: Allow ingress traffic from within the same security group
+              FromPort: 0
+              Protocol: "-1"
+              Self: true
+              ToPort: 0
+        Vpc: aws:vpc:vpc-0
+    aws:security_group:vpc-0:lambda_function_3-security_group:
+        EgressRules:
+            - CidrBlocks:
+                - 0.0.0.0/0
+              Description: Allows all outbound IPv4 traffic
+              FromPort: 0
+              Protocol: "-1"
+              ToPort: 0
+        IngressRules:
+            - Description: Allow ingress traffic from within the same security group
+              FromPort: 0
+              Protocol: "-1"
+              Self: true
+              ToPort: 0
+        Vpc: aws:vpc:vpc-0
+    aws:lambda_function:lambda_function_0:
+        EnvironmentVariables:
+            RDS_INSTANCE_1_RDS_CONNECTION_ARN: aws:rds_instance:rds-instance-1#RdsConnectionArn
+            RDS_INSTANCE_1_RDS_ENDPOINT: aws:rds_instance:rds-instance-1#Endpoint
+            RDS_INSTANCE_1_RDS_PASSWORD: aws:rds_instance:rds-instance-1#Password
+            RDS_INSTANCE_1_RDS_USERNAME: aws:rds_instance:rds-instance-1#Username
+        ExecutionRole: aws:iam_role:lambda_function_0-ExecutionRole
+        Image: aws:ecr_image:lambda_function_0-image
+        LogGroup: aws:log_group:lambda_function_0-log-group
+        MemorySize: 512
+        SecurityGroups:
+            - aws:security_group:vpc-0:lambda_function_0-security_group
+        Subnets:
+            - aws:subnet:vpc-0:subnet-0
+            - aws:subnet:vpc-0:subnet-1
+        Timeout: 180
+    aws:lambda_function:lambda_function_3:
+        EnvironmentVariables:
+            RDS_INSTANCE_1_RDS_CONNECTION_ARN: aws:rds_instance:rds-instance-1#RdsConnectionArn
+            RDS_INSTANCE_1_RDS_ENDPOINT: aws:rds_instance:rds-instance-1#Endpoint
+            RDS_INSTANCE_1_RDS_PASSWORD: aws:rds_instance:rds-instance-1#Password
+            RDS_INSTANCE_1_RDS_USERNAME: aws:rds_instance:rds-instance-1#Username
+        ExecutionRole: aws:iam_role:lambda_function_3-ExecutionRole
+        Image: aws:ecr_image:lambda_function_3-image
+        LogGroup: aws:log_group:lambda_function_3-log-group
+        MemorySize: 512
+        SecurityGroups:
+            - aws:security_group:vpc-0:lambda_function_3-security_group
+        Subnets:
+            - aws:subnet:vpc-0:subnet-0
+            - aws:subnet:vpc-0:subnet-1
+        Timeout: 180
+    aws:ecr_image:lambda_function_0-image:
+        Context: .
+        Dockerfile: lambda_function_0-image.Dockerfile
+        Repo: aws:ecr_repo:ecr_repo-0
+    aws:iam_role:lambda_function_0-ExecutionRole:
+        AssumeRolePolicyDoc:
+            Statement:
+                - Action:
+                    - sts:AssumeRole
+                  Effect: Allow
+                  Principal:
+                    Service:
+                        - lambda.amazonaws.com
+            Version: "2012-10-17"
+        InlinePolicies:
+            - Name: rds-instance-1-policy
+              Policy:
+                Statement:
+                    - Action:
+                        - rds-db:connect
+                      Effect: Allow
+                      Resource:
+                        - aws:rds_instance:rds-instance-1#RdsConnectionArn
+                Version: "2012-10-17"
+        ManagedPolicies:
+            - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
+            - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
+    aws:ecr_image:lambda_function_3-image:
+        Context: .
+        Dockerfile: lambda_function_3-image.Dockerfile
+        Repo: aws:ecr_repo:ecr_repo-0
+    aws:iam_role:lambda_function_3-ExecutionRole:
+        AssumeRolePolicyDoc:
+            Statement:
+                - Action:
+                    - sts:AssumeRole
+                  Effect: Allow
+                  Principal:
+                    Service:
+                        - lambda.amazonaws.com
+            Version: "2012-10-17"
+        InlinePolicies:
+            - Name: rds-instance-1-policy
+              Policy:
+                Statement:
+                    - Action:
+                        - rds-db:connect
+                      Effect: Allow
+                      Resource:
+                        - 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
+    aws:ecr_repo:ecr_repo-0:
+        ForceDelete: true
+    aws:elastic_ip:subnet-0-route_table-nat_gateway-elastic_ip:
+    aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip:
+    aws:log_group:lambda_function_0-log-group:
+        LogGroupName: /aws/lambda/lambda_function_0
+        RetentionInDays: 5
+    aws:log_group:lambda_function_3-log-group:
+        LogGroupName: /aws/lambda/lambda_function_3
+        RetentionInDays: 5
+    aws:nat_gateway:subnet-2:subnet-0-route_table-nat_gateway:
+        ElasticIp: aws:elastic_ip:subnet-0-route_table-nat_gateway-elastic_ip
+        Subnet: aws:subnet:vpc-0:subnet-2
+    aws:subnet:vpc-0:subnet-2:
+        AvailabilityZone: aws:availability_zone:region-0:availability_zone-0
+        CidrBlock: 10.0.0.0/18
+        MapPublicIpOnLaunch: false
+        RouteTable: aws:route_table:vpc-0:subnet-2-route_table
+        Type: public
+        Vpc: aws:vpc:vpc-0
+    aws:route_table_association:subnet-2-subnet-2-route_table:
+        RouteTable: aws:route_table:vpc-0:subnet-2-route_table
+        Subnet: aws:subnet:vpc-0:subnet-2
+    aws:route_table:vpc-0:subnet-2-route_table:
+        Routes:
+            - CidrBlock: 0.0.0.0/0
+              Gateway: aws:internet_gateway:vpc-0:internet_gateway-0
+        Vpc: aws:vpc:vpc-0
+    aws:availability_zone:region-0:availability_zone-0:
+        Index: 0
+        Region: aws:region:region-0
+    aws:internet_gateway:vpc-0:internet_gateway-0:
+        Vpc: aws:vpc:vpc-0
+    aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway:
+        ElasticIp: aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip
+        Subnet: aws:subnet:vpc-0:subnet-3
+    aws:subnet:vpc-0:subnet-3:
+        AvailabilityZone: aws:availability_zone:region-0:availability_zone-1
+        CidrBlock: 10.0.64.0/18
+        MapPublicIpOnLaunch: false
+        RouteTable: aws:route_table:vpc-0:subnet-3-route_table
+        Type: public
+        Vpc: aws:vpc:vpc-0
+    aws:route_table_association:subnet-3-subnet-3-route_table:
+        RouteTable: aws:route_table:vpc-0:subnet-3-route_table
+        Subnet: aws:subnet:vpc-0:subnet-3
+    aws:route_table:vpc-0:subnet-3-route_table:
+        Routes:
+            - CidrBlock: 0.0.0.0/0
+              Gateway: aws:internet_gateway:vpc-0:internet_gateway-0
+        Vpc: aws:vpc:vpc-0
+    aws:availability_zone:region-0:availability_zone-1:
+        Index: 1
+        Region: aws:region:region-0
+    aws:region:region-0:
+    aws:rds_instance:rds-instance-1:
+        AllocatedStorage: 20
+        DatabaseName: main
+        Engine: postgres
+        EngineVersion: "13.7"
+        IamDatabaseAuthenticationEnabled: true
+        InstanceClass: db.t3.micro
+        SecurityGroups:
+            - aws:security_group:vpc-0:rds-instance-1-security_group
+        SkipFinalSnapshot: true
+        SubnetGroup: aws:rds_subnet_group:rds_subnet_group-0
+    aws:rds_subnet_group:rds_subnet_group-0:
+        Subnets:
+            - aws:subnet:vpc-0:subnet-0
+            - aws:subnet:vpc-0:subnet-1
+    aws:subnet:vpc-0:subnet-0:
+        AvailabilityZone: aws:availability_zone:region-0:availability_zone-0
+        CidrBlock: 10.0.128.0/18
+        MapPublicIpOnLaunch: false
+        RouteTable: aws:route_table:vpc-0:subnet-0-route_table
+        Type: private
+        Vpc: aws:vpc:vpc-0
+    aws:subnet:vpc-0:subnet-1:
+        AvailabilityZone: aws:availability_zone:region-0:availability_zone-1
+        CidrBlock: 10.0.192.0/18
+        MapPublicIpOnLaunch: false
+        RouteTable: aws:route_table:vpc-0:subnet-1-route_table
+        Type: private
+        Vpc: aws:vpc:vpc-0
+    aws:route_table_association:subnet-0-subnet-0-route_table:
+        RouteTable: aws:route_table:vpc-0:subnet-0-route_table
+        Subnet: aws:subnet:vpc-0:subnet-0
+    aws:SERVICE_API:lambda_function_0-lambda_function_0-log-group:
+    aws:route_table_association:subnet-1-subnet-1-route_table:
+        RouteTable: aws:route_table:vpc-0:subnet-1-route_table
+        Subnet: aws:subnet:vpc-0:subnet-1
+    aws:security_group:vpc-0:rds-instance-1-security_group:
+        EgressRules:
+            - CidrBlocks:
+                - 0.0.0.0/0
+              Description: Allows all outbound IPv4 traffic
+              FromPort: 0
+              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
+            - 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
+              FromPort: 0
+              Protocol: "-1"
+              ToPort: 0
+        Vpc: aws:vpc:vpc-0
+    aws:route_table:vpc-0:subnet-0-route_table:
+        Routes:
+            - CidrBlock: 0.0.0.0/0
+              NatGateway: aws:nat_gateway:subnet-2:subnet-0-route_table-nat_gateway
+        Vpc: aws:vpc:vpc-0
+    aws:route_table:vpc-0:subnet-1-route_table:
+        Routes:
+            - CidrBlock: 0.0.0.0/0
+              NatGateway: aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway
+        Vpc: aws:vpc:vpc-0
+    aws:vpc:vpc-0:
+        CidrBlock: 10.0.0.0/16
+        EnableDnsHostnames: true
+        EnableDnsSupport: true
+edges:
+    aws:security_group:vpc-0:lambda_function_0-security_group -> aws:lambda_function:lambda_function_0:
+    aws:security_group:vpc-0:lambda_function_0-security_group -> aws:vpc:vpc-0:
+    aws:security_group:vpc-0:lambda_function_3-security_group -> aws:lambda_function:lambda_function_3:
+    aws:security_group:vpc-0:lambda_function_3-security_group -> aws:vpc:vpc-0:
+    aws:lambda_function:lambda_function_0 -> aws:SERVICE_API:lambda_function_0-lambda_function_0-log-group:
+    aws:lambda_function:lambda_function_0 -> aws:ecr_image:lambda_function_0-image:
+    aws:lambda_function:lambda_function_0 -> aws:iam_role:lambda_function_0-ExecutionRole:
+    aws:lambda_function:lambda_function_0 -> aws:subnet:vpc-0:subnet-0:
+    aws:lambda_function:lambda_function_0 -> aws:subnet:vpc-0:subnet-1:
+    aws:lambda_function:lambda_function_3 -> aws:SERVICE_API:lambda_function_0-lambda_function_0-log-group:
+    aws:lambda_function:lambda_function_3 -> aws:ecr_image:lambda_function_3-image:
+    aws:lambda_function:lambda_function_3 -> aws:iam_role:lambda_function_3-ExecutionRole:
+    aws:lambda_function:lambda_function_3 -> aws:subnet:vpc-0:subnet-0:
+    aws:lambda_function:lambda_function_3 -> aws:subnet:vpc-0:subnet-1:
+    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:iam_role:lambda_function_0-ExecutionRole -> aws:rds_instance:rds-instance-1:
+    aws:ecr_image:lambda_function_3-image -> aws:ecr_repo:ecr_repo-0:
+    aws:iam_role:lambda_function_3-ExecutionRole -> aws:log_group:lambda_function_3-log-group:
+    aws:nat_gateway:subnet-2:subnet-0-route_table-nat_gateway -> aws: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:subnet:vpc-0:subnet-2 -> aws:availability_zone:region-0:availability_zone-0:
+    aws:subnet:vpc-0:subnet-2 -> aws:route_table_association:subnet-2-subnet-2-route_table:
+    aws:subnet:vpc-0:subnet-2 -> aws:vpc:vpc-0:
+    aws:route_table_association:subnet-2-subnet-2-route_table -> 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 -> aws:vpc:vpc-0:
+    aws:availability_zone:region-0:availability_zone-0 -> aws:region:region-0:
+    aws:internet_gateway:vpc-0:internet_gateway-0 -> aws:vpc:vpc-0:
+    aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway -> aws: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:subnet:vpc-0:subnet-3 -> aws:availability_zone:region-0:availability_zone-1:
+    aws:subnet:vpc-0:subnet-3 -> aws:route_table_association:subnet-3-subnet-3-route_table:
+    aws:subnet:vpc-0:subnet-3 -> aws:vpc:vpc-0:
+    aws:route_table_association:subnet-3-subnet-3-route_table -> 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 -> aws:vpc:vpc-0:
+    aws:availability_zone:region-0:availability_zone-1 -> aws:region:region-0:
+    aws:rds_instance:rds-instance-1 -> aws:rds_subnet_group:rds_subnet_group-0:
+    aws:rds_subnet_group:rds_subnet_group-0 -> aws:subnet:vpc-0:subnet-0:
+    aws:rds_subnet_group:rds_subnet_group-0 -> aws:subnet:vpc-0:subnet-1:
+    aws:subnet:vpc-0:subnet-0 -> aws:SERVICE_API:lambda_function_0-lambda_function_0-log-group:
+    aws:subnet:vpc-0:subnet-0 -> aws:availability_zone:region-0:availability_zone-0:
+    aws:subnet:vpc-0:subnet-0 -> aws:route_table_association:subnet-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 -> aws:vpc:vpc-0:
+    aws:subnet:vpc-0:subnet-1 -> aws:SERVICE_API:lambda_function_0-lambda_function_0-log-group:
+    aws:subnet:vpc-0:subnet-1 -> aws:availability_zone:region-0:availability_zone-1:
+    aws:subnet:vpc-0:subnet-1 -> aws:route_table_association:subnet-1-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 -> aws:vpc:vpc-0:
+    aws:route_table_association:subnet-0-subnet-0-route_table -> aws:route_table:vpc-0:subnet-0-route_table:
+    aws:SERVICE_API:lambda_function_0-lambda_function_0-log-group -> aws:log_group:lambda_function_0-log-group:
+    aws:SERVICE_API:lambda_function_0-lambda_function_0-log-group -> aws:log_group:lambda_function_3-log-group:
+    aws:route_table_association:subnet-1-subnet-1-route_table -> aws:route_table:vpc-0:subnet-1-route_table:
+    aws:security_group:vpc-0:rds-instance-1-security_group -> aws:rds_instance:rds-instance-1:
+    aws:security_group:vpc-0:rds-instance-1-security_group -> aws:vpc:vpc-0:
+    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 -> aws:vpc:vpc-0:
+    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 -> aws:vpc:vpc-0:
diff --git a/pkg/engine2/testdata/remove_path.iac-viz.yaml b/pkg/engine2/testdata/remove_path.iac-viz.yaml
new file mode 100644
index 00000000..23072b09
--- /dev/null
+++ b/pkg/engine2/testdata/remove_path.iac-viz.yaml
@@ -0,0 +1,135 @@
+provider: aws
+resources:
+  vpc/vpc-0:
+
+  region/region-0:
+
+  aws:internet_gateway:vpc-0/internet_gateway-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: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: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: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:
+
+  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:
+
+  iam_role/lambda_function_3-executionrole:
+  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:
+
+  ecr_image/lambda_function_0-image:
+  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:
+
+  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:
+  lambda_function/lambda_function_3 -> rds_instance/rds-instance-1:
+  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:
+  lambda_function/lambda_function_0 -> rds_instance/rds-instance-1:
+  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/remove_path.input.yaml b/pkg/engine2/testdata/remove_path.input.yaml
new file mode 100644
index 00000000..9eb66ba2
--- /dev/null
+++ b/pkg/engine2/testdata/remove_path.input.yaml
@@ -0,0 +1,318 @@
+constraints:
+- operator: must_not_exist
+  scope: edge
+  target:
+    source: aws:lambda_function:lambda_function_3
+    target: aws:rds_instance:rds-instance-1
+resources:
+    aws:security_group:vpc-0:lambda_function_0-security_group:
+        EgressRules:
+            - CidrBlocks:
+                - 0.0.0.0/0
+              Description: Allows all outbound IPv4 traffic
+              FromPort: 0
+              Protocol: "-1"
+              ToPort: 0
+        IngressRules:
+            - Description: Allow ingress traffic from within the same security group
+              FromPort: 0
+              Protocol: "-1"
+              Self: true
+              ToPort: 0
+        Vpc: aws:vpc:vpc-0
+    aws:security_group:vpc-0:lambda_function_3-security_group:
+        EgressRules:
+            - CidrBlocks:
+                - 0.0.0.0/0
+              Description: Allows all outbound IPv4 traffic
+              FromPort: 0
+              Protocol: "-1"
+              ToPort: 0
+        IngressRules:
+            - Description: Allow ingress traffic from within the same security group
+              FromPort: 0
+              Protocol: "-1"
+              Self: true
+              ToPort: 0
+        Vpc: aws:vpc:vpc-0
+    aws:lambda_function:lambda_function_0:
+        EnvironmentVariables:
+            RDS_INSTANCE_1_RDS_CONNECTION_ARN: aws:rds_instance:rds-instance-1#RdsConnectionArn
+            RDS_INSTANCE_1_RDS_ENDPOINT: aws:rds_instance:rds-instance-1#Endpoint
+            RDS_INSTANCE_1_RDS_PASSWORD: aws:rds_instance:rds-instance-1#Password
+            RDS_INSTANCE_1_RDS_USERNAME: aws:rds_instance:rds-instance-1#Username
+        ExecutionRole: aws:iam_role:lambda_function_0-ExecutionRole
+        Image: aws:ecr_image:lambda_function_0-image
+        LogGroup: aws:log_group:lambda_function_0-log-group
+        MemorySize: 512
+        SecurityGroups:
+            - aws:security_group:vpc-0:lambda_function_0-security_group
+        Subnets:
+            - aws:subnet:vpc-0:subnet-0
+            - aws:subnet:vpc-0:subnet-1
+        Timeout: 180
+    aws:lambda_function:lambda_function_3:
+        EnvironmentVariables:
+            RDS_INSTANCE_1_RDS_CONNECTION_ARN: aws:rds_instance:rds-instance-1#RdsConnectionArn
+            RDS_INSTANCE_1_RDS_ENDPOINT: aws:rds_instance:rds-instance-1#Endpoint
+            RDS_INSTANCE_1_RDS_PASSWORD: aws:rds_instance:rds-instance-1#Password
+            RDS_INSTANCE_1_RDS_USERNAME: aws:rds_instance:rds-instance-1#Username
+        ExecutionRole: aws:iam_role:lambda_function_3-ExecutionRole
+        Image: aws:ecr_image:lambda_function_3-image
+        LogGroup: aws:log_group:lambda_function_3-log-group
+        MemorySize: 512
+        SecurityGroups:
+            - aws:security_group:vpc-0:lambda_function_3-security_group
+        Subnets:
+            - aws:subnet:vpc-0:subnet-0
+            - aws:subnet:vpc-0:subnet-1
+        Timeout: 180
+    aws:ecr_image:lambda_function_0-image:
+        Context: .
+        Dockerfile: lambda_function_0-image.Dockerfile
+        Repo: aws:ecr_repo:ecr_repo-0
+    aws:iam_role:lambda_function_0-ExecutionRole:
+        AssumeRolePolicyDoc:
+            Statement:
+                - Action:
+                    - sts:AssumeRole
+                  Effect: Allow
+                  Principal:
+                    Service:
+                        - lambda.amazonaws.com
+            Version: "2012-10-17"
+        InlinePolicies:
+            - Name: rds-instance-1-policy
+              Policy:
+                Statement:
+                    - Action:
+                        - rds-db:connect
+                      Effect: Allow
+                      Resource:
+                        - aws:rds_instance:rds-instance-1#RdsConnectionArn
+                Version: "2012-10-17"
+        ManagedPolicies:
+            - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
+            - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
+    aws:ecr_image:lambda_function_3-image:
+        Context: .
+        Dockerfile: lambda_function_3-image.Dockerfile
+        Repo: aws:ecr_repo:ecr_repo-0
+    aws:iam_role:lambda_function_3-ExecutionRole:
+        AssumeRolePolicyDoc:
+            Statement:
+                - Action:
+                    - sts:AssumeRole
+                  Effect: Allow
+                  Principal:
+                    Service:
+                        - lambda.amazonaws.com
+            Version: "2012-10-17"
+        InlinePolicies:
+            - Name: rds-instance-1-policy
+              Policy:
+                Statement:
+                    - Action:
+                        - rds-db:connect
+                      Effect: Allow
+                      Resource:
+                        - aws:rds_instance:rds-instance-1#RdsConnectionArn
+                Version: "2012-10-17"
+        ManagedPolicies:
+            - 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:
+    aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip:
+    aws:log_group:lambda_function_0-log-group:
+        LogGroupName: /aws/lambda/lambda_function_0
+        RetentionInDays: 5
+    aws:log_group:lambda_function_3-log-group:
+        LogGroupName: /aws/lambda/lambda_function_3
+        RetentionInDays: 5
+    aws:nat_gateway:subnet-2:subnet-0-route_table-nat_gateway:
+        ElasticIp: aws:elastic_ip:subnet-0-route_table-nat_gateway-elastic_ip
+        Subnet: aws:subnet:vpc-0:subnet-2
+    aws:subnet:vpc-0:subnet-2:
+        AvailabilityZone: aws:availability_zone:region-0:availability_zone-0
+        CidrBlock: 10.0.0.0/18
+        MapPublicIpOnLaunch: false
+        RouteTable: aws:route_table:vpc-0:subnet-2-route_table
+        Type: public
+        Vpc: aws:vpc:vpc-0
+    aws:route_table_association:subnet-2-subnet-2-route_table:
+        RouteTable: aws:route_table:vpc-0:subnet-2-route_table
+        Subnet: aws:subnet:vpc-0:subnet-2
+    aws:route_table:vpc-0:subnet-2-route_table:
+        Routes:
+            - CidrBlock: 0.0.0.0/0
+              Gateway: aws:internet_gateway:vpc-0:internet_gateway-0
+        Vpc: aws:vpc:vpc-0
+    aws:availability_zone:region-0:availability_zone-0:
+        Index: 0
+        Region: aws:region:region-0
+    aws:internet_gateway:vpc-0:internet_gateway-0:
+        Vpc: aws:vpc:vpc-0
+    aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway:
+        ElasticIp: aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip
+        Subnet: aws:subnet:vpc-0:subnet-3
+    aws:subnet:vpc-0:subnet-3:
+        AvailabilityZone: aws:availability_zone:region-0:availability_zone-1
+        CidrBlock: 10.0.64.0/18
+        MapPublicIpOnLaunch: false
+        RouteTable: aws:route_table:vpc-0:subnet-3-route_table
+        Type: public
+        Vpc: aws:vpc:vpc-0
+    aws:route_table_association:subnet-3-subnet-3-route_table:
+        RouteTable: aws:route_table:vpc-0:subnet-3-route_table
+        Subnet: aws:subnet:vpc-0:subnet-3
+    aws:route_table:vpc-0:subnet-3-route_table:
+        Routes:
+            - CidrBlock: 0.0.0.0/0
+              Gateway: aws:internet_gateway:vpc-0:internet_gateway-0
+        Vpc: aws:vpc:vpc-0
+    aws:availability_zone:region-0:availability_zone-1:
+        Index: 1
+        Region: aws:region:region-0
+    aws:region:region-0:
+    aws:rds_instance:rds-instance-1:
+        AllocatedStorage: 20
+        DatabaseName: main
+        Engine: postgres
+        EngineVersion: "13.7"
+        IamDatabaseAuthenticationEnabled: true
+        InstanceClass: db.t3.micro
+        SecurityGroups:
+            - aws:security_group:vpc-0:rds-instance-1-security_group
+        SkipFinalSnapshot: true
+        SubnetGroup: aws:rds_subnet_group:rds_subnet_group-0
+    aws:rds_subnet_group:rds_subnet_group-0:
+        Subnets:
+            - aws:subnet:vpc-0:subnet-0
+            - aws:subnet:vpc-0:subnet-1
+    aws:subnet:vpc-0:subnet-0:
+        AvailabilityZone: aws:availability_zone:region-0:availability_zone-0
+        CidrBlock: 10.0.128.0/18
+        MapPublicIpOnLaunch: false
+        RouteTable: aws:route_table:vpc-0:subnet-0-route_table
+        Type: private
+        Vpc: aws:vpc:vpc-0
+    aws:subnet:vpc-0:subnet-1:
+        AvailabilityZone: aws:availability_zone:region-0:availability_zone-1
+        CidrBlock: 10.0.192.0/18
+        MapPublicIpOnLaunch: false
+        RouteTable: aws:route_table:vpc-0:subnet-1-route_table
+        Type: private
+        Vpc: aws:vpc:vpc-0
+    aws:route_table_association:subnet-0-subnet-0-route_table:
+        RouteTable: aws:route_table:vpc-0:subnet-0-route_table
+        Subnet: aws:subnet:vpc-0:subnet-0
+    aws:SERVICE_API:lambda_function_0-lambda_function_0-log-group:
+    aws:route_table_association:subnet-1-subnet-1-route_table:
+        RouteTable: aws:route_table:vpc-0:subnet-1-route_table
+        Subnet: aws:subnet:vpc-0:subnet-1
+    aws:security_group:vpc-0:rds-instance-1-security_group:
+        EgressRules:
+            - CidrBlocks:
+                - 0.0.0.0/0
+              Description: Allows all outbound IPv4 traffic
+              FromPort: 0
+              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
+              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:
+            - CidrBlock: 0.0.0.0/0
+              NatGateway: aws:nat_gateway:subnet-2:subnet-0-route_table-nat_gateway
+        Vpc: aws:vpc:vpc-0
+    aws:route_table:vpc-0:subnet-1-route_table:
+        Routes:
+            - CidrBlock: 0.0.0.0/0
+              NatGateway: aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway
+        Vpc: aws:vpc:vpc-0
+    aws:vpc:vpc-0:
+        CidrBlock: 10.0.0.0/16
+        EnableDnsHostnames: true
+        EnableDnsSupport: true
+edges:
+    aws:security_group:vpc-0:lambda_function_0-security_group -> aws:lambda_function:lambda_function_0:
+    aws:security_group:vpc-0:lambda_function_0-security_group -> aws:vpc:vpc-0:
+    aws:security_group:vpc-0:lambda_function_3-security_group -> aws:lambda_function:lambda_function_3:
+    aws:security_group:vpc-0:lambda_function_3-security_group -> aws:vpc:vpc-0:
+    aws:lambda_function:lambda_function_0 -> aws:SERVICE_API:lambda_function_0-lambda_function_0-log-group:
+    aws:lambda_function:lambda_function_0 -> aws:ecr_image:lambda_function_0-image:
+    aws:lambda_function:lambda_function_0 -> aws:iam_role:lambda_function_0-ExecutionRole:
+    aws:lambda_function:lambda_function_0 -> aws:subnet:vpc-0:subnet-0:
+    aws:lambda_function:lambda_function_0 -> aws:subnet:vpc-0:subnet-1:
+    aws:lambda_function:lambda_function_3 -> aws:SERVICE_API:lambda_function_0-lambda_function_0-log-group:
+    aws:lambda_function:lambda_function_3 -> aws:ecr_image:lambda_function_3-image:
+    aws:lambda_function:lambda_function_3 -> aws:iam_role:lambda_function_3-ExecutionRole:
+    aws:lambda_function:lambda_function_3 -> aws:subnet:vpc-0:subnet-0:
+    aws:lambda_function:lambda_function_3 -> aws:subnet:vpc-0:subnet-1:
+    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:iam_role:lambda_function_0-ExecutionRole -> aws:rds_instance:rds-instance-1:
+    aws:ecr_image:lambda_function_3-image -> aws:ecr_repo:ecr_repo-0:
+    aws:iam_role:lambda_function_3-ExecutionRole -> aws:log_group:lambda_function_3-log-group:
+    aws:iam_role:lambda_function_3-ExecutionRole -> aws:rds_instance:rds-instance-1:
+    aws:nat_gateway:subnet-2:subnet-0-route_table-nat_gateway -> aws: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:subnet:vpc-0:subnet-2 -> aws:availability_zone:region-0:availability_zone-0:
+    aws:subnet:vpc-0:subnet-2 -> aws:route_table_association:subnet-2-subnet-2-route_table:
+    aws:subnet:vpc-0:subnet-2 -> aws:vpc:vpc-0:
+    aws:route_table_association:subnet-2-subnet-2-route_table -> 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 -> aws:vpc:vpc-0:
+    aws:availability_zone:region-0:availability_zone-0 -> aws:region:region-0:
+    aws:internet_gateway:vpc-0:internet_gateway-0 -> aws:vpc:vpc-0:
+    aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway -> aws: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:subnet:vpc-0:subnet-3 -> aws:availability_zone:region-0:availability_zone-1:
+    aws:subnet:vpc-0:subnet-3 -> aws:route_table_association:subnet-3-subnet-3-route_table:
+    aws:subnet:vpc-0:subnet-3 -> aws:vpc:vpc-0:
+    aws:route_table_association:subnet-3-subnet-3-route_table -> 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 -> aws:vpc:vpc-0:
+    aws:availability_zone:region-0:availability_zone-1 -> aws:region:region-0:
+    aws:rds_instance:rds-instance-1 -> aws:rds_subnet_group:rds_subnet_group-0:
+    aws:rds_subnet_group:rds_subnet_group-0 -> aws:subnet:vpc-0:subnet-0:
+    aws:rds_subnet_group:rds_subnet_group-0 -> aws:subnet:vpc-0:subnet-1:
+    aws:subnet:vpc-0:subnet-0 -> aws:SERVICE_API:lambda_function_0-lambda_function_0-log-group:
+    aws:subnet:vpc-0:subnet-0 -> aws:availability_zone:region-0:availability_zone-0:
+    aws:subnet:vpc-0:subnet-0 -> aws:route_table_association:subnet-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 -> aws:vpc:vpc-0:
+    aws:subnet:vpc-0:subnet-1 -> aws:SERVICE_API:lambda_function_0-lambda_function_0-log-group:
+    aws:subnet:vpc-0:subnet-1 -> aws:availability_zone:region-0:availability_zone-1:
+    aws:subnet:vpc-0:subnet-1 -> aws:route_table_association:subnet-1-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 -> aws:vpc:vpc-0:
+    aws:route_table_association:subnet-0-subnet-0-route_table -> aws:route_table:vpc-0:subnet-0-route_table:
+    aws:SERVICE_API:lambda_function_0-lambda_function_0-log-group -> aws:log_group:lambda_function_0-log-group:
+    aws:SERVICE_API:lambda_function_0-lambda_function_0-log-group -> aws:log_group:lambda_function_3-log-group:
+    aws:route_table_association:subnet-1-subnet-1-route_table -> aws:route_table:vpc-0:subnet-1-route_table:
+    aws:security_group:vpc-0:rds-instance-1-security_group -> aws:rds_instance:rds-instance-1:
+    aws:security_group:vpc-0:rds-instance-1-security_group -> aws:vpc:vpc-0:
+    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 -> aws:vpc:vpc-0:
+    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 -> aws:vpc:vpc-0: