Repository: klothoplatform/klotho
Pull request #622 (2023-05-22 15:11:24)

address all comments from pr chains
• Does any part of it require special attention?
• Does it relate to or fix any issue?

addressing the actionable comments to unblock.

Creates new knowledge base pkg outside of core so we have some distinction. The kb methods now are on the kb object itself.

Some small changes surrounding caching in the kb methods to reduce allocation.

remove debug logging

other various small fixes


### 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/go.mod b/go.mod
index 6bff0fc0..f11675ba 100644
--- a/go.mod
+++ b/go.mod
@@ -42,6 +42,7 @@ require (
 	github.com/kr/pretty v0.3.0 // indirect
 	github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
 	github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
+	github.com/mitchellh/mapstructure v1.5.0 // indirect
 	github.com/rivo/uniseg v0.4.4 // indirect
 	golang.org/x/mod v0.9.0 // indirect
 	golang.org/x/oauth2 v0.4.0 // indirect
diff --git a/go.sum b/go.sum
index da7582a5..f8434add 100644
--- a/go.sum
+++ b/go.sum
@@ -521,6 +521,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
 github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
 github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
diff --git a/pkg/core/annotation.go b/pkg/core/annotation.go
index 181cb122..df4db772 100644
--- a/pkg/core/annotation.go
+++ b/pkg/core/annotation.go
@@ -49,6 +49,17 @@ func (a *Annotation) MarshalJSON() ([]byte, error) {
 	return json.Marshal(m)
 }
 
+func DedupeAnnotationKeys(a []AnnotationKey) []AnnotationKey {
+	result := make(map[AnnotationKey]struct{})
+	for _, key := range a {
+		result[key] = struct{}{}
+	}
+	keys := make([]AnnotationKey, 0, len(result))
+	for key := range result {
+		keys = append(keys, key)
+	}
+	return keys
+}
 func (a Annotation) Format(s fmt.State, verb rune) {
 	fmt.Fprintf(s, "@klotho::%s", a.Capability.Name)
 	if len(a.Capability.Directives) > 0 {
diff --git a/pkg/core/construct_graph.go b/pkg/core/construct_graph.go
index d3219713..b993bbd9 100644
--- a/pkg/core/construct_graph.go
+++ b/pkg/core/construct_graph.go
@@ -36,7 +36,7 @@ func (cg *ConstructGraph) AddConstruct(construct Construct) {
 }
 
 func (cg *ConstructGraph) AddDependency(source string, dest string) {
-	cg.underlying.AddEdge(source, dest)
+	cg.underlying.AddEdge(source, dest, nil)
 }
 
 func (cg *ConstructGraph) GetConstruct(key string) Construct {
diff --git a/pkg/core/resource_graph.go b/pkg/core/resource_graph.go
index d3e3260b..8e93be1b 100644
--- a/pkg/core/resource_graph.go
+++ b/pkg/core/resource_graph.go
@@ -1,9 +1,12 @@
 package core
 
 import (
+	"fmt"
 	"reflect"
 
 	"github.com/klothoplatform/klotho/pkg/graph"
+	"github.com/klothoplatform/klotho/pkg/multierr"
+	"github.com/pkg/errors"
 	"go.uber.org/zap"
 )
 
@@ -47,13 +50,30 @@ func (rg *ResourceGraph) AddResource(resource Resource) {
 //
 //	rg.AddDependency(lambda, lambda.Role)
 func (rg *ResourceGraph) AddDependency(deployedSecond Resource, deployedFirst Resource) {
+
+	rg.AddResource(deployedSecond)
+	rg.AddResource(deployedFirst)
+
+	if cycle, _ := rg.underlying.CreatesCycle(deployedSecond.Id().String(), deployedFirst.Id().String()); cycle {
+		zap.S().Errorf("Not Adding Dependency, Cycle would be created from edge %s -> %s", deployedSecond.Id(), deployedFirst.Id())
+	} else {
+		rg.underlying.AddEdge(deployedSecond.Id().String(), deployedFirst.Id().String(), nil)
+		zap.S().Debugf("adding %s -> %s", deployedSecond.Id(), deployedFirst.Id())
+	}
+}
+
+// AddDependencyWithData Adds a dependency such that `deployedSecond` has to be deployed after `deployedFirst`. This makes the left-to-right
+// association consistent with our visualizer, and with the Go struct graph.
+// This method also allows any edge data to be attached to the dependency in the ResourceGraph
+func (rg *ResourceGraph) AddDependencyWithData(deployedSecond Resource, deployedFirst Resource, data any) {
+
 	for _, res := range []Resource{deployedSecond, deployedFirst} {
 		rg.AddResource(res)
 	}
 	if cycle, _ := rg.underlying.CreatesCycle(deployedSecond.Id().String(), deployedFirst.Id().String()); cycle {
 		zap.S().Errorf("Not Adding Dependency, Cycle would be created from edge %s -> %s", deployedSecond.Id(), deployedFirst.Id())
 	} else {
-		rg.underlying.AddEdge(deployedSecond.Id().String(), deployedFirst.Id().String())
+		rg.underlying.AddEdge(deployedSecond.Id().String(), deployedFirst.Id().String(), data)
 		zap.S().Debugf("adding %s -> %s", deployedSecond.Id(), deployedFirst.Id())
 	}
 }
@@ -74,6 +94,10 @@ func (rg *ResourceGraph) GetDependencyByVertexIds(source string, target string)
 	return rg.underlying.GetEdge(source, target)
 }
 
+func (rg *ResourceGraph) RemoveDependency(source string, target string) error {
+	return rg.underlying.RemoveEdge(source, target)
+}
+
 func (rg *ResourceGraph) ListResources() []Resource {
 	return rg.underlying.GetAllVertices()
 }
@@ -235,3 +259,245 @@ func (rg *ResourceGraph) getAllDownstreamResourcesSet(source Resource, upstreams
 	}
 	return upstreams
 }
+
+// CreateDependencies takes in a resource and set of metadata and looks at any fields which point to specific dependencies which match the Resource Interface
+// If a specific dependency is found, the .Create method will be called to create the parent resource.
+// Each specific dependency value will be updated on the resources field itself
+// This method will recurse down each one of the resources fields and do a DFS until a Resource is found
+//
+// Before adding a dependency and setting the value on the resource's field, the method will ensure there is no existing node in the graph for the named node created
+// If there is an existing node, the method will ensure the resource's field is set to point to the already existent node
+//
+// IaCValues today are not labeled as a specific resource, so the value must already be present. This method will still add the dependency and ensure the resource is created based on the params passed in, but
+// does not have the knowledge of which resource to create
+//
+//	T params are a map of the reflection field names, to their respective fields
+//
+// Example:
+// for a struct:
+//
+//	 Test{
+//	   Field1 Resource
+//	   Field2 Resource
+//	}
+//
+// The corresponding params would be:
+//
+//	params := map[string]any{
+//	   "Field1": ResourceParams
+//	   "Field2": ResourceParams
+//	}
+//
+// Params for direct resources or IaCValues correlate to the field they are for
+// For params on fields which correlate to the following types, the formats are:
+//   - Map: map[string]ParamType    (the string key corresponds to the key in the map)
+//   - Struct: map[string]ParamType (the string key corresponds to the field in the struct)
+//   - Array or slice: []ParamType
+func (rg *ResourceGraph) CreateDependencies(res Resource, params map[string]any) error {
+	var merr multierr.Error
+	source := reflect.ValueOf(res)
+
+	for source.Kind() == reflect.Pointer {
+		source = source.Elem()
+	}
+	for i := 0; i < source.NumField(); i++ {
+		targetValue := source.Field(i)
+		fieldsParams := params[source.Type().Field(i).Name]
+		if fieldsParams != nil {
+			merr.Append(rg.actOnValue(targetValue, res, fieldsParams, nil, reflect.Value{}))
+		}
+	}
+	rg.AddDependenciesReflect(res)
+	return merr.ErrOrNil()
+}
+
+// CreateResource is a wrapper around a Resources .Create method
+//
+// CreateResource provides safety in assuring that the up to date resource (which is inline with what exists in the ResourceGraph) is returned
+func CreateResource[T Resource](rg *ResourceGraph, params any) (resource T, err error) {
+
+	res := reflect.New(reflect.TypeOf(resource).Elem()).Interface()
+	err = rg.callCreate(reflect.ValueOf(res), params)
+	if err != nil {
+		return
+	}
+	castedRes, ok := res.(Resource)
+	if !ok {
+		err = fmt.Errorf("unable to cast to type Resource")
+		return
+	}
+
+	currValue := rg.GetResourceByVertexId(castedRes.Id().String())
+	if currValue != nil {
+		return currValue.(T), nil
+	}
+	return castedRes.(T), nil
+}
+
+func (rg *ResourceGraph) actOnValue(targetValue reflect.Value, res Resource, metadata any, parent *reflect.Value, index reflect.Value) error {
+	switch value := targetValue.Interface().(type) {
+	case Resource:
+		if targetValue.IsNil() {
+			value = reflect.New(targetValue.Type().Elem()).Interface().(Resource)
+		}
+		err := rg.callCreate(reflect.ValueOf(value), metadata)
+		currValue := rg.GetResourceByVertexId(value.Id().String())
+		if currValue != nil {
+			value = currValue
+		}
+		if err == nil && value != nil {
+			if parent != nil {
+				parent.SetMapIndex(index, reflect.ValueOf(value))
+			} else {
+				targetValue.Set(reflect.ValueOf(value))
+			}
+		} else {
+			return err
+		}
+	case *IaCValue:
+		if value != nil && value.Resource != nil {
+			err := rg.callCreate(reflect.ValueOf(value.Resource), metadata)
+			currValue := rg.GetResourceByVertexId(value.Resource.Id().String())
+			if currValue != nil {
+				value.Resource = currValue
+			}
+			if err == nil && value.Resource != nil {
+				if parent != nil {
+					parent.SetMapIndex(index, reflect.ValueOf(value))
+				} else {
+					targetValue.Set(reflect.ValueOf(value))
+				}
+			} else {
+				return err
+			}
+		}
+	case IaCValue:
+		if value.Resource != nil {
+			err := rg.callCreate(reflect.ValueOf(value.Resource), metadata)
+			currValue := rg.GetResourceByVertexId(value.Resource.Id().String())
+			if currValue != nil {
+				value.Resource = currValue
+			}
+			if err == nil && value.Resource != nil {
+				if parent != nil {
+					parent.SetMapIndex(index, reflect.ValueOf(value))
+				} else {
+					targetValue.Set(reflect.ValueOf(value))
+				}
+			} else {
+				return err
+			}
+		}
+	default:
+		correspondingValue := targetValue
+		for correspondingValue.Kind() == reflect.Pointer {
+			correspondingValue = targetValue.Elem()
+		}
+
+		err := rg.checkChild(correspondingValue, res, metadata)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (rg *ResourceGraph) callCreate(targetValue reflect.Value, metadata any) error {
+	method := targetValue.MethodByName("Create")
+	if method.IsValid() {
+		var callArgs []reflect.Value
+		callArgs = append(callArgs, reflect.ValueOf(rg))
+		params := reflect.New(method.Type().In(1)).Interface()
+		decoder := GetMapDecoder(params)
+		err := decoder.Decode(metadata)
+		if err != nil {
+			return errors.Wrap(err, fmt.Sprintf("error decoding the following type %s", reflect.New(method.Type().In(1)).Type().String()))
+		}
+		callArgs = append(callArgs, reflect.ValueOf(params).Elem())
+		eval := method.Call(callArgs)
+		if eval[0].IsNil() {
+			return nil
+		} else {
+			err, ok := eval[0].Interface().(error)
+			if !ok {
+				return fmt.Errorf("return type should be an error")
+			}
+			return err
+		}
+	}
+	return nil
+}
+
+// CallConfigure uses the resource graph to ensure the node passed in exists, then uses reflection to call the resources Configure method
+func (rg *ResourceGraph) CallConfigure(resource Resource, metadata any) error {
+
+	if rg.GetResource(resource.Id()) == nil {
+		return fmt.Errorf("resource with id %s cannot be configured since it does not exist in the ResourceGraph", resource.Id())
+	}
+
+	method := reflect.ValueOf(resource).MethodByName("Configure")
+	if method.IsValid() {
+		var callArgs []reflect.Value
+		params := reflect.New(method.Type().In(0)).Interface()
+		decoder := GetMapDecoder(params)
+		err := decoder.Decode(metadata)
+		if err != nil {
+			return errors.Wrapf(err, fmt.Sprintf("error decoding the following type %s", reflect.New(method.Type().In(0)).Type()))
+		}
+		callArgs = append(callArgs, reflect.ValueOf(params).Elem())
+		eval := method.Call(callArgs)
+		if eval[0].IsNil() {
+			return nil
+		} else {
+			err, ok := eval[0].Interface().(error)
+			if !ok {
+				return fmt.Errorf("return type should be an error")
+			}
+			return err
+		}
+	}
+	return nil
+}
+
+func (rg *ResourceGraph) checkChild(child reflect.Value, res Resource, metadata any) error {
+	var merr multierr.Error
+	switch child.Kind() {
+	case reflect.Struct:
+		params := reflect.ValueOf(metadata)
+		if params.Kind() != reflect.Map {
+			return fmt.Errorf("field %s params does not conform to type for structs", child.Type().String())
+		}
+		for i := 0; i < child.NumField(); i++ {
+			childVal := child.Field(i)
+			fieldName := child.Type().Field(i).Name
+
+			// Loop over the keys of the params map and see if anything correlates to the field in the struct. If so then we will act on that field of the struct
+			for _, key := range params.MapKeys() {
+				if key.String() == fieldName {
+					merr.Append(rg.actOnValue(childVal, res, params.MapIndex(reflect.ValueOf(fieldName)).Interface(), nil, reflect.Value{}))
+				}
+			}
+		}
+	case reflect.Slice, reflect.Array:
+		params := reflect.ValueOf(metadata)
+		if params.Kind() != reflect.Slice && params.Kind() != reflect.Array {
+			return fmt.Errorf("field %s does not match parent type %s", params.Type().String(), child.Type().String())
+		} else if params.Len() != child.Len() {
+			return fmt.Errorf("field %s does not have the same number of elements as parent", child.Type().String())
+		}
+		for elemIdx := 0; elemIdx < child.Len(); elemIdx++ {
+			elemValue := child.Index(elemIdx)
+			merr.Append(rg.actOnValue(elemValue, res, params.Index(elemIdx).Interface(), nil, reflect.Value{}))
+		}
+	case reflect.Map:
+		params := reflect.ValueOf(metadata)
+		if params.Kind() != reflect.Map {
+			return fmt.Errorf("field %s params does not conform to type for maps", child.Type().String())
+		}
+		for _, key := range child.MapKeys() {
+			elemValue := child.MapIndex(key)
+			merr.Append(rg.actOnValue(elemValue, res, params.MapIndex(key).Interface(), &child, key))
+		}
+	}
+	return merr.ErrOrNil()
+}
diff --git a/pkg/core/resources.go b/pkg/core/resources.go
index 514f2334..3bc56f94 100644
--- a/pkg/core/resources.go
+++ b/pkg/core/resources.go
@@ -4,6 +4,7 @@ import (
 	"strings"
 
 	"github.com/klothoplatform/klotho/pkg/graph"
+	"github.com/mitchellh/mapstructure"
 	"github.com/pkg/errors"
 )
 
@@ -107,3 +108,11 @@ func (id ResourceId) MarshalTOML() ([]byte, error) {
 func (id *ResourceId) UnmarshalTOML(data []byte) error {
 	return id.UnmarshalText(data)
 }
+
+func GetMapDecoder(result interface{}) *mapstructure.Decoder {
+	decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ErrorUnset: true, Result: result})
+	if err != nil {
+		panic(err)
+	}
+	return decoder
+}
diff --git a/pkg/graph/graph.go b/pkg/graph/graph.go
index 7dd0016e..291e30b5 100644
--- a/pkg/graph/graph.go
+++ b/pkg/graph/graph.go
@@ -21,6 +21,7 @@ type (
 	Edge[V any] struct {
 		Source      V
 		Destination V
+		Properties  graph.EdgeProperties
 	}
 )
 
@@ -103,8 +104,8 @@ func (d *Directed[V]) AddVerticesAndEdge(source V, dest V) {
 	}
 }
 
-func (d *Directed[V]) AddEdge(source string, dest string) {
-	err := d.underlying.AddEdge(source, dest)
+func (d *Directed[V]) AddEdge(source string, dest string, data any) {
+	err := d.underlying.AddEdge(source, dest, graph.EdgeData(data))
 	if err != nil && !errors.Is(err, graph.ErrEdgeAlreadyExists) {
 		zap.S().With("error", zap.Error(err)).Errorf(
 			`Unexpected error while adding edge between "%v" and "%v"`, source, dest)
@@ -148,7 +149,7 @@ func (d *Directed[V]) GetEdge(source string, target string) *Edge[V] {
 	v, err := d.underlying.Edge(source, target)
 	switch {
 	case err == nil:
-		return &Edge[V]{Source: v.Source, Destination: v.Target}
+		return &Edge[V]{Source: v.Source, Destination: v.Target, Properties: v.Properties}
 
 	case errors.Is(err, graph.ErrEdgeNotFound):
 		return nil
@@ -160,6 +161,10 @@ func (d *Directed[V]) GetEdge(source string, target string) *Edge[V] {
 	}
 }
 
+func (d *Directed[V]) RemoveEdge(source string, target string) error {
+	return d.underlying.RemoveEdge(source, target)
+}
+
 func (d *Directed[V]) IdForNode(v V) string {
 	return d.hasher(v)
 }
@@ -185,7 +190,7 @@ func (d *Directed[V]) GetAllEdges() []Edge[V] {
 				zap.S().With(zap.Error(err)).Errorf(
 					`Ignoring edge %v because I couldn't resolve the destination vertex. %s`, edge, ourFault)
 			}
-			results = append(results, Edge[V]{Source: sourceV, Destination: destV})
+			results = append(results, Edge[V]{Source: sourceV, Destination: destV, Properties: edge.Properties})
 		}
 	}
 	return results
diff --git a/pkg/knowledge_base/edge_kb.go b/pkg/knowledge_base/edge_kb.go
new file mode 100644
index 00000000..b735492c
--- /dev/null
+++ b/pkg/knowledge_base/edge_kb.go
@@ -0,0 +1,304 @@
+package knowledgebase
+
+import (
+	"fmt"
+	"reflect"
+
+	"github.com/klothoplatform/klotho/pkg/core"
+	"github.com/klothoplatform/klotho/pkg/multierr"
+	"go.uber.org/zap"
+)
+
+type (
+	// Edge defines an entry in a Knowledge base. An Edge represents a valid linking between two types of resources
+	Edge struct {
+		// Source represents the source resource in the edge
+		Source reflect.Type
+		// Destination represents the target resource in the edge
+		Destination reflect.Type
+	}
+
+	// EdgeDetails defines the set of characteristics and edge in the knowledge base contains. The details are used to ensure graph correctness for ResourceGraphs
+	EdgeDetails struct {
+		// ExpansionFunc is a function used to create the To and From resource and necessary intermediate resources, if any do not exist, to ensure the nodes are in place for correct functionality.
+		ExpansionFunc ExpandEdge
+		// Configure is a function used to configure the To and From resources and necessary dependent resources, to ensure the nodes will guarantee correct functionality.
+		Configure ConfigureEdge
+		// ValidDestinations is a list of end destinations the edge supports. This field is used within determining the path (edge expansion) between two resources.
+		ValidDestinations []reflect.Type
+	}
+
+	// EdgeKB is a map (knowledge base) of edges and their respective details used to configure ResourceGraphs
+	EdgeKB map[Edge]EdgeDetails
+
+	// EdgeExpander is a function used to create the To and From resource and necessary intermediate resources, if any do not exist, to ensure the nodes are in place for correct functionality.
+	ExpandEdge func(source, dest core.Resource, dag *core.ResourceGraph, data EdgeData) error
+	// EdgeConfigurer is a function used to configure the To and From resources and necessary dependent resources, to ensure the nodes will guarantee correct functionality.
+	ConfigureEdge func(source, dest core.Resource, dag *core.ResourceGraph, data EdgeData) error
+
+	// EdgeConstraint is an object defined on EdgeData which can influence the path picked when expansion occurs.
+	EdgeConstraint struct {
+		// NodeMustExist specifies a list of resources which must exist in the path when edge expansion occurs. The resources type will be correlated to the types in the generated paths
+		NodeMustExist []core.Resource
+		// NodeMustNotExist specifies a list of resources which must not exist when edge expansion occurs. The resources type will be correlated to the types in the generated paths
+		NodeMustNotExist []core.Resource
+	}
+
+	// EdgeData is an object attached to edges in the ResourceGraph to help the knowledge base understand context when performing expansion and configuration tasks
+	EdgeData struct {
+		// AppName refers to the application name of the global ResourceGraph
+		AppName string
+		// EnvironmentVaribles specify and environment variables which will need to be configured during the edge expansion process
+		EnvironmentVariables []core.EnvironmentVariable
+		// Constraint refers to the EdgeConstraints defined during the edge expansion
+		Constraint EdgeConstraint
+		// Source refers to the initial source resource node when edge expansion is called
+		Source core.Resource
+		// Destination refers to the initial target resource node when edge expansion is called
+		Destination core.Resource
+	}
+
+	Path []Edge
+)
+
+// GetEdgeDetails takes in a source and target to retrieve the edge details for the given key. Will return nil if no edge exists for the given source and target
+func (kb EdgeKB) GetEdgeDetails(source reflect.Type, target reflect.Type) EdgeDetails {
+	return kb[Edge{Source: source, Destination: target}]
+}
+
+// GetEdgesWithSource will return all edges where the source type parameter is the From of the edge
+func (kb EdgeKB) GetEdgesWithSource(source reflect.Type) []Edge {
+	result := []Edge{}
+	for edge := range kb {
+		if edge.Source == source {
+			result = append(result, edge)
+		}
+	}
+	return result
+}
+
+// GetEdgesWithTarget will return all edges where the target type parameter is the To of the edge
+func (kb EdgeKB) GetEdgesWithTarget(target reflect.Type) []Edge {
+	result := []Edge{}
+	for edge := range kb {
+		if edge.Destination == target {
+			result = append(result, edge)
+		}
+	}
+	return result
+}
+
+// FindPaths takes in a source and destination type and finds all valid paths to get from source to destination.
+//
+// Find paths does a Depth First Search to search through all edges in the knowledge base.
+// The function tracks visited edges to prevent cycles during execution
+// It also checks the ValidDestinations for each edge against the original destination node to ensure that the edge is allowed to be used in the instance of the path generation
+//
+// The method will return all paths found
+func (kb EdgeKB) FindPaths(source reflect.Type, dest reflect.Type) []Path {
+	zap.S().Debugf("Finding Paths from %s -> %s", source, dest)
+	visitedEdges := map[reflect.Type]bool{}
+	stack := []Edge{}
+	return kb.findPaths(source, dest, stack, visitedEdges)
+}
+
+// findPaths performs the recursive calls of the parent FindPath function
+//
+// It works under the assumption that an edge is bidirectional and uses the edges ValidDestinations field to determine when that assumption is incorrect
+func (kb EdgeKB) findPaths(source reflect.Type, dest reflect.Type, stack []Edge, visited map[reflect.Type]bool) (result []Path) {
+	visited[source] = true
+	if source == dest {
+		result = append(result, stack)
+	} else {
+		for _, e := range kb.GetEdgesWithSource(source) {
+			if e.Source == source && !visited[e.Destination] && kb.isValidForPath(e, dest) {
+				result = append(result, kb.findPaths(e.Destination, dest, append(stack, e), visited)...)
+			}
+		}
+		for _, e := range kb.GetEdgesWithTarget(source) {
+			if e.Destination == source && !visited[e.Source] && kb.isValidForPath(e, dest) {
+				result = append(result, kb.findPaths(e.Source, dest, append(stack, e), visited)...)
+			}
+		}
+	}
+	delete(visited, source)
+	return result
+}
+
+// isValidForPath determines if an edge is valid for an instance of path generation.
+//
+// The criteria is:
+//   - if there is no expansion function and no ValidDestinations, assume all destinations are valid.
+//   - otherwise check to see if the path generations destination is valid for the edge
+func (kb EdgeKB) isValidForPath(edge Edge, dest reflect.Type) bool {
+	edgeDetail := kb.GetEdgeDetails(edge.Source, edge.Destination)
+	if edgeDetail.ExpansionFunc == nil || len(edgeDetail.ValidDestinations) == 0 {
+		return true
+	}
+	for _, validDest := range edgeDetail.ValidDestinations {
+		if validDest == dest {
+			return true
+		}
+	}
+	return false
+}
+
+// ExpandEdges performs calculations to determine the proper path to be inserted into the ResourceGraph.
+//
+// The workflow of the edge expansion is as follows:
+//   - Find all valid paths from the dependencies source node to the dependencies target node
+//   - Check each of the valid paths against constraints passed in on the edge data
+//   - At this point we should only have 1 valid path (If we have more than 1 edge choose direct connection otherwise error)
+//   - Iterate through each edge in path calling expansion function on edge
+func (kb EdgeKB) ExpandEdges(dag *core.ResourceGraph) (err error) {
+	zap.S().Debug("Expanding Edges")
+	var merr multierr.Error
+	// It does not matter what order we go in as each edge should be expanded independently. They can still reuse resources since the create methods should be idempotent if resources are the same.
+	for _, dep := range dag.ListDependencies() {
+		zap.S().Debug("Expanding Edge for %s -> %s", dep.Source.Id(), dep.Destination.Id())
+
+		// We want to retrieve the edge data from the edge in the resource graph to use during expansion
+		edgeData := EdgeData{}
+		data, ok := dep.Properties.Data.(EdgeData)
+		if !ok && dep.Properties.Data != nil {
+			merr.Append(fmt.Errorf("edge properties for edge %s -> %s, do not satisfy edge data format", dep.Source.Id(), dep.Destination.Id()))
+		} else if dep.Properties.Data != nil {
+			edgeData = data
+		}
+		// Find all possible paths given the initial source and destination node
+		paths := kb.FindPaths(reflect.TypeOf(dep.Source), reflect.TypeOf(dep.Destination))
+		validPaths := [][]Edge{}
+		for _, path := range paths {
+
+			// Ensure that the path satisfies the NodeMustExist edge constraint
+			if edgeData.Constraint.NodeMustExist != nil {
+				nodeFound := false
+				for _, res := range path {
+					for _, mustExistRes := range edgeData.Constraint.NodeMustExist {
+						if res.Source == reflect.TypeOf(mustExistRes) || res.Destination == reflect.TypeOf(mustExistRes) {
+							nodeFound = true
+						}
+					}
+				}
+				if !nodeFound {
+					continue
+				}
+			}
+
+			// Ensure that the path satisfies the NodeMustNotExist edge constraint
+			if edgeData.Constraint.NodeMustNotExist != nil {
+				nodeFound := false
+				for _, res := range path {
+					for _, mustNotExistRes := range edgeData.Constraint.NodeMustNotExist {
+						if res.Source == reflect.TypeOf(mustNotExistRes) || res.Destination == reflect.TypeOf(mustNotExistRes) {
+							nodeFound = true
+						}
+					}
+				}
+				if nodeFound {
+					continue
+				}
+			}
+			validPaths = append(validPaths, path)
+		}
+
+		zap.S().Debugf("Found valid paths %s", validPaths)
+		var validPath []Edge
+
+		// If we have more than 1 valid path we will always default to the direct path if it exists, otherwise we will raise an error since we cannot determine which path we are supposed to use.
+		if len(validPaths) > 1 {
+			for _, p := range validPaths {
+				if len(p) == 2 {
+					zap.S().Debug("Defaulting to direct path")
+					validPath = p
+				}
+			}
+			if len(validPath) == 0 {
+				merr.Append(fmt.Errorf("found multiple paths which satisfy constraints for edge %s -> %s. \n Paths: %s", dep.Source.Id(), dep.Destination.Id(), validPaths))
+			}
+		} else if len(validPaths) == 0 {
+			merr.Append(fmt.Errorf("found no paths which satisfy constraints for edge %s -> %s. \n Paths: %s", dep.Source.Id(), dep.Destination.Id(), validPaths))
+		} else {
+			validPath = validPaths[0]
+
+			// If the valid path is not the original direct path, we want to remove the initial direct dependency so we can fill in the new edges with intermediate nodes
+			if len(validPath) > 2 {
+				zap.S().Debugf("Removing dependency from %s -> %s", dep.Source.Id(), dep.Destination.Id())
+				err := dag.RemoveDependency(dep.Source.Id().String(), dep.Destination.Id().String())
+				if err != nil {
+					merr.Append(err)
+					continue
+				}
+			}
+
+			// resourceCache is used to always pass the graphs nodes into the Expand functions if they exist. We do this so that we operate on nodes which already exist
+			resourceCache := map[reflect.Type]core.Resource{}
+			for _, edge := range validPath {
+				source := edge.Source
+				dest := edge.Destination
+				edgeDetail := kb.GetEdgeDetails(source, dest)
+				sourceNode := resourceCache[source]
+				if source == reflect.TypeOf(dep.Source) {
+					sourceNode = dep.Source
+				}
+				if sourceNode == nil {
+					sourceNode = reflect.New(source.Elem()).Interface().(core.Resource)
+				}
+
+				destNode := resourceCache[dest]
+				if dest == reflect.TypeOf(dep.Destination) {
+					destNode = dep.Destination
+				}
+				if destNode == nil {
+					destNode = reflect.New(dest.Elem()).Interface().(core.Resource)
+				}
+
+				if edgeDetail.ExpansionFunc != nil {
+					err := edgeDetail.ExpansionFunc(sourceNode, destNode, dag, edgeData)
+					merr.Append(err)
+				}
+
+				if sourceNode != nil {
+					resourceCache[source] = sourceNode
+				}
+				sourceNodeInGraph := dag.GetResource(sourceNode.Id())
+				if sourceNodeInGraph != nil {
+					resourceCache[source] = sourceNodeInGraph
+				}
+				if destNode != nil {
+					resourceCache[dest] = destNode
+				}
+				destNodeInGraph := dag.GetResource(destNode.Id())
+				if destNodeInGraph != nil {
+					resourceCache[dest] = destNodeInGraph
+				}
+			}
+		}
+
+	}
+	return merr.ErrOrNil()
+}
+
+// ConfigureFromEdgeData calls each edges configure function.
+func (kb EdgeKB) ConfigureFromEdgeData(dag *core.ResourceGraph) (err error) {
+	zap.S().Debug("Configuring Edges")
+	var merr multierr.Error
+	for _, dep := range dag.ListDependencies() {
+		zap.S().Debugf("Configuring Edge for %s -> %s", dep.Source.Id(), dep.Destination.Id())
+		to := reflect.TypeOf(dep.Source)
+		from := reflect.TypeOf(dep.Destination)
+		edgeData := EdgeData{}
+		data, ok := dep.Properties.Data.(EdgeData)
+		if !ok && dep.Properties.Data != nil {
+			merr.Append(fmt.Errorf("edge properties for edge %s -> %s, do not satisfy edge data format", dep.Source.Id(), dep.Destination.Id()))
+		} else if dep.Properties.Data != nil {
+			edgeData = data
+		}
+		edgeDetail := kb.GetEdgeDetails(to, from)
+		if edgeDetail.Configure != nil {
+			err := edgeDetail.Configure(dep.Source, dep.Destination, dag, edgeData)
+			merr.Append(err)
+		}
+	}
+	return merr.ErrOrNil()
+}
diff --git a/pkg/provider/aws/execution_unit.go b/pkg/provider/aws/execution_unit.go
index 4656efe9..155e9f74 100644
--- a/pkg/provider/aws/execution_unit.go
+++ b/pkg/provider/aws/execution_unit.go
@@ -12,6 +12,52 @@ import (
 	"go.uber.org/zap"
 )
 
+// expandExecutionUnit takes in a single execution unit and expands the generic construct into a set of resource's based on the units configuration.
+func (a *AWS) expandExecutionUnit(dag *core.ResourceGraph, unit *core.ExecutionUnit) error {
+	switch a.Config.GetExecutionUnit(unit.ID).Type {
+	case Lambda:
+		lambda, err := core.CreateResource[*resources.LambdaFunction](dag, resources.LambdaCreateParams{
+			AppName: a.Config.AppName,
+			Refs:    []core.AnnotationKey{unit.AnnotationKey},
+			Name:    unit.ID,
+		})
+		if err != nil {
+			return err
+		}
+		err = a.MapResourceToConstruct(lambda, unit)
+		if err != nil {
+			return err
+		}
+	default:
+		return fmt.Errorf("unsupported execution unit type %s", a.Config.GetExecutionUnit(unit.ID).Type)
+	}
+	return nil
+}
+
+func (a *AWS) getLambdaConfiguration(result *core.ConstructGraph, dag *core.ResourceGraph, refs []core.AnnotationKey) (resources.LambdaFunctionConfigureParams, error) {
+	if len(refs) != 1 {
+		return resources.LambdaFunctionConfigureParams{}, fmt.Errorf("lambda must only have one construct reference")
+	}
+	lambdaConfig := resources.LambdaFunctionConfigureParams{}
+	construct := result.GetConstruct(refs[0].ToId())
+	if construct == nil {
+		return resources.LambdaFunctionConfigureParams{}, fmt.Errorf("construct with id %s does not exist", refs[0].ToId())
+	}
+	unit, ok := construct.(*core.ExecutionUnit)
+	if !ok {
+		return resources.LambdaFunctionConfigureParams{}, fmt.Errorf("lambda must only have a construct reference to an execution unit")
+	}
+	for _, env := range unit.EnvironmentVariables {
+		if env.Construct == nil {
+			lambdaConfig.EnvironmentVariables = append(lambdaConfig.EnvironmentVariables, env)
+		}
+	}
+	cfg := config.ConvertFromInfraParams[config.ServerlessTypeParams](a.Config.GetExecutionUnit(refs[0].ID).InfraParams)
+	lambdaConfig.MemorySize = cfg.Memory
+	lambdaConfig.Timeout = cfg.Timeout
+	return lambdaConfig, nil
+}
+
 // GenerateExecUnitResources generates the necessary AWS resources for a given execution unit and adds them to the resource graph
 func (a *AWS) GenerateExecUnitResources(unit *core.ExecutionUnit, result *core.ConstructGraph, dag *core.ResourceGraph) error {
 	log := zap.S()
diff --git a/pkg/provider/aws/knowledgebase/edges.go b/pkg/provider/aws/knowledgebase/edges.go
new file mode 100644
index 00000000..5bc2a32c
--- /dev/null
+++ b/pkg/provider/aws/knowledgebase/edges.go
@@ -0,0 +1,245 @@
+package knowledgebase
+
+import (
+	"fmt"
+	"reflect"
+
+	"github.com/klothoplatform/klotho/pkg/core"
+	knowledgebase "github.com/klothoplatform/klotho/pkg/knowledge_base"
+	"github.com/klothoplatform/klotho/pkg/provider/aws/resources"
+)
+
+var AwsKB = knowledgebase.EdgeKB{
+	knowledgebase.Edge{Source: reflect.TypeOf(&resources.LambdaFunction{}), Destination: reflect.TypeOf(&resources.RdsInstance{})}: knowledgebase.EdgeDetails{
+		ExpansionFunc: func(source, dest core.Resource, dag *core.ResourceGraph, data knowledgebase.EdgeData) error {
+			lambda := source.(*resources.LambdaFunction)
+			instance := dest.(*resources.RdsInstance)
+			if len(lambda.Subnets) == 0 {
+				lambda.Subnets = instance.SubnetGroup.Subnets
+			}
+			if len(lambda.SecurityGroups) == 0 {
+				lambda.SecurityGroups = instance.SecurityGroups
+			}
+			return nil
+		},
+		Configure: func(source, dest core.Resource, dag *core.ResourceGraph, data knowledgebase.EdgeData) error {
+			lambda := source.(*resources.LambdaFunction)
+			instance := dest.(*resources.RdsInstance)
+			if len(lambda.Subnets) == 0 {
+				return fmt.Errorf("unable Destination expand edge [%s -> %s]: lambda function [%s] is not in a VPC",
+					lambda.Id().String(), instance.Id().String(), lambda.Id().String())
+			}
+			inlinePol := resources.NewIamInlinePolicy(fmt.Sprintf("%s-connectionpolicy", instance.Name), core.DedupeAnnotationKeys(append(lambda.ConstructsRef, instance.ConstructsRef...)), instance.GetConnectionPolicyDocument())
+			lambda.Role.InlinePolicies = append(lambda.Role.InlinePolicies, inlinePol)
+
+			for _, env := range data.EnvironmentVariables {
+				lambda.EnvironmentVariables[env.GetName()] = core.IaCValue{Resource: instance, Property: env.GetValue()}
+			}
+			return nil
+		},
+	},
+	knowledgebase.Edge{Source: reflect.TypeOf(&resources.LambdaFunction{}), Destination: reflect.TypeOf(&resources.RdsProxy{})}: knowledgebase.EdgeDetails{
+		ExpansionFunc: func(source, dest core.Resource, dag *core.ResourceGraph, data knowledgebase.EdgeData) error {
+			lambda := source.(*resources.LambdaFunction)
+			proxy := dest.(*resources.RdsProxy)
+			if len(lambda.Subnets) == 0 {
+				lambda.Subnets = proxy.Subnets
+			}
+			if len(lambda.SecurityGroups) == 0 {
+				lambda.SecurityGroups = proxy.SecurityGroups
+			}
+			return nil
+		},
+		Configure: func(source, dest core.Resource, dag *core.ResourceGraph, data knowledgebase.EdgeData) error {
+			lambda := source.(*resources.LambdaFunction)
+			proxy := dest.(*resources.RdsProxy)
+			if len(lambda.Subnets) == 0 {
+				return fmt.Errorf("unable Destination expand edge [%s -> %s]: lambda function [%s] is not in a VPC",
+					lambda.Id().String(), proxy.Id().String(), lambda.Id().String())
+			}
+
+			upstreamResources := dag.GetUpstreamResources(proxy)
+			for _, res := range upstreamResources {
+				if tg, ok := res.(*resources.RdsProxyTargetGroup); ok {
+					for _, tgUpstream := range dag.GetDownstreamResources(tg) {
+						if instance, ok := tgUpstream.(*resources.RdsInstance); ok {
+							inlinePol := resources.NewIamInlinePolicy(fmt.Sprintf("%s-connectionpolicy", instance.Name),
+								core.DedupeAnnotationKeys(append(lambda.ConstructsRef, instance.ConstructsRef...)), instance.GetConnectionPolicyDocument())
+							lambda.Role.InlinePolicies = append(lambda.Role.InlinePolicies, inlinePol)
+						}
+					}
+				}
+			}
+
+			for _, env := range data.EnvironmentVariables {
+				lambda.EnvironmentVariables[env.GetName()] = core.IaCValue{Resource: proxy, Property: env.GetValue()}
+			}
+			return nil
+		},
+	},
+	knowledgebase.Edge{Source: reflect.TypeOf(&resources.RdsProxyTargetGroup{}), Destination: reflect.TypeOf(&resources.RdsInstance{})}: knowledgebase.EdgeDetails{
+		ExpansionFunc: func(source, dest core.Resource, dag *core.ResourceGraph, data knowledgebase.EdgeData) error {
+			instance := dest.(*resources.RdsInstance)
+			targetGroup := source.(*resources.RdsProxyTargetGroup)
+			if targetGroup.Name == "" {
+				proxyTargetGroup, err := core.CreateResource[*resources.RdsProxyTargetGroup](dag, resources.RdsProxyTargetGroupCreateParams{
+					AppName: data.AppName,
+					Refs:    instance.ConstructsRef,
+					Name:    instance.Name,
+				})
+				if err != nil {
+					return err
+				}
+				dag.AddDependency(proxyTargetGroup, instance)
+			}
+			return nil
+		},
+		Configure: func(source, dest core.Resource, dag *core.ResourceGraph, data knowledgebase.EdgeData) error {
+			instance := dest.(*resources.RdsInstance)
+			targetGroup := source.(*resources.RdsProxyTargetGroup)
+
+			if targetGroup.RdsInstance == nil {
+				targetGroup.RdsInstance = instance
+			} else if targetGroup.RdsInstance != instance {
+				return fmt.Errorf("target group, %s, has edge Destination instance, %s, but internal property is set Destination a different instance", targetGroup.Name, instance.Name)
+			}
+
+			secret := targetGroup.RdsProxy.Auths[0].SecretArn.Resource.(*resources.Secret)
+			for _, res := range dag.GetDownstreamResources(secret) {
+				if secretVersion, ok := res.(*resources.SecretVersion); ok {
+					secretVersion.Path = instance.CredentialsFile.Path()
+				}
+			}
+			return nil
+		},
+		ValidDestinations: []reflect.Type{reflect.TypeOf(&resources.RdsInstance{})},
+	},
+	knowledgebase.Edge{Source: reflect.TypeOf(&resources.RdsProxyTargetGroup{}), Destination: reflect.TypeOf(&resources.RdsProxy{})}: knowledgebase.EdgeDetails{
+		ExpansionFunc: func(source, dest core.Resource, dag *core.ResourceGraph, data knowledgebase.EdgeData) error {
+			proxy := dest.(*resources.RdsProxy)
+			targetGroup := source.(*resources.RdsProxyTargetGroup)
+			destination := data.Destination.(*resources.RdsInstance)
+			if proxy.Name == "" {
+				var err error
+				proxy, err = core.CreateResource[*resources.RdsProxy](dag, resources.RdsProxyCreateParams{
+					AppName: data.AppName,
+					Refs:    targetGroup.ConstructsRef,
+					Name:    destination.Name,
+				})
+				if err != nil {
+					return err
+				}
+			}
+
+			if targetGroup.Name == "" {
+				proxyTargetGroup, err := core.CreateResource[*resources.RdsProxyTargetGroup](dag, resources.RdsProxyTargetGroupCreateParams{
+					AppName: data.AppName,
+					Refs:    proxy.ConstructsRef,
+					Name:    destination.Name,
+				})
+				if err != nil {
+					return err
+				}
+				dag.AddDependency(proxyTargetGroup, proxy)
+			}
+			secretVersion, err := core.CreateResource[*resources.SecretVersion](dag, resources.SecretVersionCreateParams{
+				AppName: data.AppName,
+				Refs:    core.DedupeAnnotationKeys(append(source.KlothoConstructRef(), dest.KlothoConstructRef()...)),
+				Name:    proxy.Name,
+			})
+			if err != nil {
+				return err
+			}
+			proxy.Auths = append(proxy.Auths, &resources.ProxyAuth{
+				AuthScheme: "SECRETS",
+				IamAuth:    "DISABLED",
+				SecretArn:  core.IaCValue{Resource: secretVersion.Secret, Property: resources.ARN_IAC_VALUE},
+			})
+			dag.AddDependency(proxy, secretVersion)
+
+			secretPolicy, err := core.CreateResource[*resources.IamPolicy](dag, resources.IamPolicyCreateParams{
+				AppName: data.AppName,
+				Name:    fmt.Sprintf("%s-ormsecretpolicy", proxy.Name),
+				Refs:    proxy.ConstructsRef,
+			})
+			if err != nil {
+				return err
+			}
+			dag.AddDependency(secretPolicy, secretVersion)
+			dag.AddDependency(proxy.Role, secretPolicy)
+			dag.AddDependenciesReflect(proxy)
+			return nil
+		},
+		Configure: func(source, dest core.Resource, dag *core.ResourceGraph, data knowledgebase.EdgeData) error {
+			proxy := dest.(*resources.RdsProxy)
+			targetGroup := source.(*resources.RdsProxyTargetGroup)
+
+			if targetGroup.RdsProxy == nil {
+				targetGroup.RdsProxy = proxy
+			} else if targetGroup.RdsProxy != proxy {
+				return fmt.Errorf("target group, %s, has edge Destination proxy, %s, but internal property is set Destination a different proxy", targetGroup.Name, proxy.Name)
+			}
+			return nil
+		},
+		ValidDestinations: []reflect.Type{reflect.TypeOf(&resources.RdsInstance{})},
+	},
+	knowledgebase.Edge{Source: reflect.TypeOf(&resources.RdsProxy{}), Destination: reflect.TypeOf(&resources.SecretVersion{})}: knowledgebase.EdgeDetails{
+		ExpansionFunc: func(source, dest core.Resource, dag *core.ResourceGraph, data knowledgebase.EdgeData) error {
+			proxy := dest.(*resources.RdsProxy)
+			secretVersion, err := core.CreateResource[*resources.SecretVersion](dag, resources.SecretVersionCreateParams{
+				AppName: data.AppName,
+				Refs:    core.DedupeAnnotationKeys(append(source.KlothoConstructRef(), dest.KlothoConstructRef()...)),
+				Name:    proxy.Name,
+			})
+			if err != nil {
+				return err
+			}
+			dag.AddDependency(proxy, secretVersion)
+
+			secretPolicy, err := core.CreateResource[*resources.IamPolicy](dag, resources.IamPolicyCreateParams{
+				AppName: data.AppName,
+				Name:    fmt.Sprintf("%s-ormsecretpolicy", proxy.Name),
+				Refs:    proxy.ConstructsRef,
+			})
+			if err != nil {
+				return err
+			}
+			dag.AddDependency(secretPolicy, secretVersion)
+			dag.AddDependency(proxy.Role, secretPolicy)
+			return nil
+		},
+		Configure: func(source, dest core.Resource, dag *core.ResourceGraph, data knowledgebase.EdgeData) error {
+			secretVersion := dest.(*resources.SecretVersion)
+			secretVersion.Type = "string"
+			return nil
+		},
+	},
+	knowledgebase.Edge{Source: reflect.TypeOf(&resources.IamPolicy{}), Destination: reflect.TypeOf(&resources.SecretVersion{})}: knowledgebase.EdgeDetails{
+		Configure: func(source, dest core.Resource, dag *core.ResourceGraph, data knowledgebase.EdgeData) error {
+			policy := source.(*resources.IamPolicy)
+			secretVersion := dest.(*resources.SecretVersion)
+			secretPolicyDoc := resources.CreateAllowPolicyDocument([]string{"secretsmanager:GetSecretValue"}, []core.IaCValue{{Resource: secretVersion.Secret, Property: resources.ARN_IAC_VALUE}})
+			if policy.Policy == nil {
+				policy.Policy = secretPolicyDoc
+			} else {
+				policy.Policy.Statement = append(policy.Policy.Statement, secretPolicyDoc.Statement...)
+			}
+			return nil
+		},
+	},
+	knowledgebase.Edge{Source: reflect.TypeOf(&resources.IamRole{}), Destination: reflect.TypeOf(&resources.IamPolicy{})}: knowledgebase.EdgeDetails{
+		Configure: func(source, dest core.Resource, dag *core.ResourceGraph, data knowledgebase.EdgeData) error {
+			policy := dest.(*resources.IamPolicy)
+			role := source.(*resources.IamRole)
+			role.ManagedPolicies = append(role.ManagedPolicies, core.IaCValue{Resource: policy, Property: resources.ARN_IAC_VALUE})
+			return nil
+		},
+	},
+	knowledgebase.Edge{Source: reflect.TypeOf(&resources.RdsInstance{}), Destination: reflect.TypeOf(&resources.RdsSubnetGroup{})}: knowledgebase.EdgeDetails{},
+	knowledgebase.Edge{Source: reflect.TypeOf(&resources.RdsInstance{}), Destination: reflect.TypeOf(&resources.SecurityGroup{})}:  knowledgebase.EdgeDetails{},
+	knowledgebase.Edge{Source: reflect.TypeOf(&resources.LambdaFunction{}), Destination: reflect.TypeOf(&resources.IamRole{})}:     knowledgebase.EdgeDetails{},
+	knowledgebase.Edge{Source: reflect.TypeOf(&resources.LambdaFunction{}), Destination: reflect.TypeOf(&resources.EcrImage{})}:    knowledgebase.EdgeDetails{},
+	knowledgebase.Edge{Source: reflect.TypeOf(&resources.EcrImage{}), Destination: reflect.TypeOf(&resources.EcrRepository{})}:     knowledgebase.EdgeDetails{},
+	knowledgebase.Edge{Source: reflect.TypeOf(&resources.SecurityGroup{}), Destination: reflect.TypeOf(&resources.Vpc{})}:          knowledgebase.EdgeDetails{},
+	knowledgebase.Edge{Source: reflect.TypeOf(&resources.Subnet{}), Destination: reflect.TypeOf(&resources.Vpc{})}:                 knowledgebase.EdgeDetails{},
+}
diff --git a/pkg/provider/aws/kv.go b/pkg/provider/aws/kv.go
index 75a4585a..9a70a093 100644
--- a/pkg/provider/aws/kv.go
+++ b/pkg/provider/aws/kv.go
@@ -46,7 +46,7 @@ func (a *AWS) GenerateKvResources(kv *core.Kv, result *core.ConstructGraph, dag
 				{Resource: table, Property: resources.DYNAMODB_TABLE_STREAM_IAC_VALUE},
 			}
 			policyDoc := resources.CreateAllowPolicyDocument(actions, policyResources)
-			policy := resources.NewIamInlinePolicy(fmt.Sprintf("%s-kv-dynamodb", unit.ID), kv.Provenance(), policyDoc)
+			policy := resources.NewIamInlinePolicy(fmt.Sprintf("%s-kv-dynamodb", unit.ID), []core.AnnotationKey{kv.Provenance()}, policyDoc)
 			a.PolicyGenerator.AddInlinePolicyToUnit(unit.Id(), policy)
 		}
 	}
diff --git a/pkg/provider/aws/orm.go b/pkg/provider/aws/orm.go
index c78b5147..eaa121bc 100644
--- a/pkg/provider/aws/orm.go
+++ b/pkg/provider/aws/orm.go
@@ -7,6 +7,28 @@ import (
 	"github.com/klothoplatform/klotho/pkg/provider/aws/resources"
 )
 
+// expandOrm takes in a single orm construct and expands the generic construct into a set of resource's based on the units configuration.
+func (a *AWS) expandOrm(dag *core.ResourceGraph, orm *core.Orm) error {
+	switch a.Config.GetPersistOrm(orm.ID).Type {
+	case Rds_postgres:
+		instance, err := core.CreateResource[*resources.RdsInstance](dag, resources.RdsInstanceCreateParams{
+			AppName: a.Config.AppName,
+			Refs:    []core.AnnotationKey{orm.AnnotationKey},
+			Name:    orm.ID,
+		})
+		if err != nil {
+			return err
+		}
+		err = a.MapResourceToConstruct(instance, orm)
+		if err != nil {
+			return err
+		}
+	default:
+		return fmt.Errorf("unsupported orm type %s", a.Config.GetPersistOrm(orm.ID).Type)
+	}
+	return nil
+}
+
 func (a *AWS) GenerateOrmResources(construct *core.Orm, result *core.ConstructGraph, dag *core.ResourceGraph) error {
 	vpc := resources.GetVpc(a.Config, dag)
 	securityGroups := []*resources.SecurityGroup{resources.GetSecurityGroup(a.Config, dag)}
@@ -25,7 +47,7 @@ func (a *AWS) GenerateOrmResources(construct *core.Orm, result *core.ConstructGr
 		unit, ok := res.(*core.ExecutionUnit)
 		if ok {
 			a.PolicyGenerator.AddInlinePolicyToUnit(unit.Id(),
-				resources.NewIamInlinePolicy(fmt.Sprintf("%s-connectionpolicy", instance.Name), unit.Provenance(), policyDoc))
+				resources.NewIamInlinePolicy(fmt.Sprintf("%s-connectionpolicy", instance.Name), []core.AnnotationKey{unit.Provenance()}, policyDoc))
 		}
 	}
 	return nil
diff --git a/pkg/provider/aws/persist.go b/pkg/provider/aws/persist.go
index 8ae1b6b4..795b43bc 100644
--- a/pkg/provider/aws/persist.go
+++ b/pkg/provider/aws/persist.go
@@ -17,7 +17,7 @@ func (a *AWS) GenerateFsResources(construct core.Construct, result *core.Constru
 		{Resource: bucket, Property: resources.ALL_BUCKET_DIRECTORY_IAC_VALUE},
 	}
 	policyDoc := resources.CreateAllowPolicyDocument(actions, policyResources)
-	policy := resources.NewIamInlinePolicy(fmt.Sprintf("%s-s3", construct.Id()), construct.Provenance(), policyDoc)
+	policy := resources.NewIamInlinePolicy(fmt.Sprintf("%s-s3", construct.Id()), []core.AnnotationKey{construct.Provenance()}, policyDoc)
 	upstreamResources := result.GetUpstreamConstructs(construct)
 	for _, res := range upstreamResources {
 		unit, ok := res.(*core.ExecutionUnit)
diff --git a/pkg/provider/aws/plugin.go b/pkg/provider/aws/plugin.go
index 9d3b1cab..b81304b5 100644
--- a/pkg/provider/aws/plugin.go
+++ b/pkg/provider/aws/plugin.go
@@ -5,12 +5,81 @@ import (
 
 	"github.com/klothoplatform/klotho/pkg/core"
 	"github.com/klothoplatform/klotho/pkg/infra/kubernetes"
+	knowledgebase "github.com/klothoplatform/klotho/pkg/knowledge_base"
 	"github.com/klothoplatform/klotho/pkg/multierr"
 	"github.com/klothoplatform/klotho/pkg/provider/aws/resources"
 	"github.com/pkg/errors"
 	"go.uber.org/zap"
 )
 
+// ExpandConstructs looks at all existing constructs in the construct graph and turns them into their respective AWS Resources
+func (a *AWS) ExpandConstructs(result *core.ConstructGraph, dag *core.ResourceGraph) (err error) {
+	log := zap.S()
+	var merr multierr.Error
+	for _, construct := range result.ListConstructs() {
+		log.Debugf("Converting construct with id, %s, to aws resources", construct.Id())
+		switch construct := construct.(type) {
+		case *core.ExecutionUnit:
+			merr.Append(a.expandExecutionUnit(dag, construct))
+		case *core.Orm:
+			merr.Append(a.expandOrm(dag, construct))
+		}
+	}
+	return merr.ErrOrNil()
+}
+
+// CopyConstructEdgesToDag looks at the dependencies which existed in the construct graph and copies those dependencies into the resource graph so that the edges can be later expanded on
+func (a *AWS) CopyConstructEdgesToDag(result *core.ConstructGraph, dag *core.ResourceGraph) (err error) {
+	var merr multierr.Error
+	for _, dep := range result.ListDependencies() {
+		sourceResource := a.GetResourceTiedToConstruct(dep.Source)
+		if sourceResource == nil {
+			merr.Append(errors.Errorf("unable to copy edge, no resource tied to construct %s", dep.Source.Id()))
+			continue
+		}
+		targetResource := a.GetResourceTiedToConstruct(dep.Destination)
+		if targetResource == nil {
+			merr.Append(errors.Errorf("unable to copy edge, no resource tied to construct %s", dep.Destination.Id()))
+			continue
+		}
+
+		data := knowledgebase.EdgeData{AppName: a.Config.AppName, Source: sourceResource, Destination: targetResource}
+		if unit, ok := dep.Source.(*core.ExecutionUnit); ok {
+			if _, ok := dep.Destination.(*core.Orm); ok {
+				data.Constraint = knowledgebase.EdgeConstraint{
+					NodeMustExist:    []core.Resource{&resources.RdsProxy{}},
+					NodeMustNotExist: []core.Resource{&resources.IamRole{}},
+				}
+			}
+			for _, envVar := range unit.EnvironmentVariables {
+				if envVar.Construct == dep.Destination {
+					data.EnvironmentVariables = []core.EnvironmentVariable{envVar}
+				}
+			}
+		}
+		dag.AddDependencyWithData(sourceResource, targetResource, data)
+	}
+	return merr.ErrOrNil()
+}
+
+// configureResources calls every resource's Configure method, for resources that exist in the graph
+func (a *AWS) configureResources(result *core.ConstructGraph, dag *core.ResourceGraph) (err error) {
+	var merr multierr.Error
+	for _, resource := range dag.ListResources() {
+		var configuration any
+		switch res := resource.(type) {
+		case *resources.LambdaFunction:
+			configuration, err = a.getLambdaConfiguration(result, dag, res.ConstructsRef)
+			if err != nil {
+				merr.Append(err)
+				continue
+			}
+		}
+		merr.Append(dag.CallConfigure(resource, configuration))
+	}
+	return merr.ErrOrNil()
+}
+
 func (a *AWS) Translate(result *core.ConstructGraph, dag *core.ResourceGraph) (links []core.CloudResourceLink, err error) {
 	log := zap.S()
 
diff --git a/pkg/provider/aws/provider.go b/pkg/provider/aws/provider.go
index af06c9e5..7f957bac 100644
--- a/pkg/provider/aws/provider.go
+++ b/pkg/provider/aws/provider.go
@@ -11,6 +11,7 @@ import (
 type AWS struct {
 	Config                 *config.Application
 	constructIdToResources map[string][]core.Resource
+	constructIdToResource  map[string]core.Resource
 	PolicyGenerator        *resources.PolicyGenerator
 }
 
@@ -47,3 +48,16 @@ func (a *AWS) GetResourcesDirectlyTiedToConstruct(construct core.Construct) ([]c
 	resources, found := a.constructIdToResources[construct.Id()]
 	return resources, found
 }
+
+func (a *AWS) MapResourceToConstruct(resource core.Resource, construct core.Construct) error {
+	if a.constructIdToResource == nil {
+		a.constructIdToResource = make(map[string]core.Resource)
+	}
+	a.constructIdToResource[construct.Id()] = resource
+	return nil
+}
+
+func (a *AWS) GetResourceTiedToConstruct(construct core.Construct) core.Resource {
+	resource := a.constructIdToResource[construct.Id()]
+	return resource
+}
diff --git a/pkg/provider/aws/resources/cloudwatch.go b/pkg/provider/aws/resources/cloudwatch.go
index f4f9df0a..1b3694c9 100644
--- a/pkg/provider/aws/resources/cloudwatch.go
+++ b/pkg/provider/aws/resources/cloudwatch.go
@@ -20,6 +20,26 @@ type (
 	}
 )
 
+type CloudwatchLogGroupCreateParams struct {
+	AppName string
+	Refs    []core.AnnotationKey
+	Name    string
+}
+
+func (logGroup *LogGroup) Create(dag *core.ResourceGraph, params CloudwatchLogGroupCreateParams) error {
+	logGroup.Name = logGroupSanitizer.Apply(fmt.Sprintf("%s-%s", params.AppName, params.Name))
+	logGroup.ConstructsRef = params.Refs
+
+	existingLogGroup := dag.GetResource(logGroup.Id())
+	if existingLogGroup != nil {
+		graphLogGroup := existingLogGroup.(*LogGroup)
+		graphLogGroup.ConstructsRef = core.DedupeAnnotationKeys(append(graphLogGroup.KlothoConstructRef(), params.Refs...))
+	} else {
+		dag.AddResource(logGroup)
+	}
+	return nil
+}
+
 func NewLogGroup(appName string, logGroupName string, ref core.AnnotationKey, retention int) *LogGroup {
 	return &LogGroup{
 		Name:            logGroupSanitizer.Apply(fmt.Sprintf("%s-%s", appName, logGroupName)),
diff --git a/pkg/provider/aws/resources/ecr.go b/pkg/provider/aws/resources/ecr.go
index 6e872eae..dc5500d7 100644
--- a/pkg/provider/aws/resources/ecr.go
+++ b/pkg/provider/aws/resources/ecr.go
@@ -30,6 +30,69 @@ type (
 	}
 )
 
+type RepoCreateParams struct {
+	AppName string
+	Refs    []core.AnnotationKey
+}
+
+func (repo *EcrRepository) Create(dag *core.ResourceGraph, params RepoCreateParams) error {
+	repo.Name = params.AppName
+	repo.ConstructsRef = params.Refs
+
+	existingRepo := dag.GetResource(repo.Id())
+	if existingRepo != nil {
+		graphRepo := existingRepo.(*EcrRepository)
+		graphRepo.ConstructsRef = core.DedupeAnnotationKeys(append(graphRepo.KlothoConstructRef(), params.Refs...))
+	} else {
+		dag.AddResource(repo)
+	}
+	return nil
+}
+
+type EcrRepositoryConfigureParams struct {
+	ForceDelete bool
+}
+
+func (repo *EcrRepository) Configure(params EcrRepositoryConfigureParams) error {
+	repo.ForceDelete = true
+	return nil
+}
+
+type ImageCreateParams struct {
+	AppName string
+	Refs    []core.AnnotationKey
+	Name    string
+}
+
+func (image *EcrImage) Create(dag *core.ResourceGraph, params ImageCreateParams) error {
+	name := fmt.Sprintf("%s-%s", params.AppName, params.Name)
+	image.Name = name
+	image.ConstructsRef = params.Refs
+
+	existingImage := dag.GetResource(image.Id())
+	if existingImage != nil {
+		return fmt.Errorf("ecr image with name %s already exists", name)
+	}
+
+	err := dag.CreateDependencies(image, map[string]any{
+		"Repo": RepoCreateParams{
+			AppName: params.AppName,
+			Refs:    params.Refs,
+		},
+	})
+	return err
+}
+
+type EcrImageConfigureParams struct {
+	ExtraOptions []string
+}
+
+// Configure sets the intristic characteristics of a vpc based on parameters passed in
+func (image *EcrImage) Configure(params EcrImageConfigureParams) error {
+	image.ExtraOptions = []string{"--platform", "linux/amd64", "--quiet"}
+	return nil
+}
+
 func GenerateEcrRepoAndImage(appName string, unit *core.ExecutionUnit, dag *core.ResourceGraph) (*EcrImage, error) {
 	// See if we have already created an ecr repository for the app and if not create one, otherwise add a ref to this exec unit
 	var repo *EcrRepository
diff --git a/pkg/provider/aws/resources/eks.go b/pkg/provider/aws/resources/eks.go
index ac68b7f3..a3630f40 100644
--- a/pkg/provider/aws/resources/eks.go
+++ b/pkg/provider/aws/resources/eks.go
@@ -753,7 +753,7 @@ func createClusterAdminRole(appName string, roleName string, refs []core.Annotat
 func createPodExecutionRole(appName string, roleName string, refs []core.AnnotationKey) *IamRole {
 	fargateRole := NewIamRole(appName, roleName, refs, EKS_FARGATE_ASSUME_ROLE_POLICY)
 	fargateRole.InlinePolicies = []*IamInlinePolicy{
-		NewIamInlinePolicy("fargate-pod-execution-policy", refs[0],
+		NewIamInlinePolicy("fargate-pod-execution-policy", refs,
 			&PolicyDocument{Version: VERSION, Statement: []StatementEntry{
 				{
 					Effect: "Allow",
diff --git a/pkg/provider/aws/resources/iam.go b/pkg/provider/aws/resources/iam.go
index 3ebbbf2a..ab085d04 100644
--- a/pkg/provider/aws/resources/iam.go
+++ b/pkg/provider/aws/resources/iam.go
@@ -147,6 +147,43 @@ type (
 	}
 )
 
+type RoleCreateParams struct {
+	AppName string
+	Name    string
+	Refs    []core.AnnotationKey
+}
+
+// Create takes in an all necessary parameters to generate the Roles name and ensure that the Role is correlated to the constructs which required its creation.
+func (role *IamRole) Create(dag *core.ResourceGraph, params RoleCreateParams) error {
+	role.Name = roleSanitizer.Apply(fmt.Sprintf("%s-%s", params.AppName, params.Name))
+	role.ConstructsRef = params.Refs
+
+	existingRole := dag.GetResource(role.Id())
+	if existingRole != nil {
+		return fmt.Errorf("iam role with name %s already exists", role.Name)
+	}
+
+	dag.AddResource(role)
+	return nil
+}
+
+type IamPolicyCreateParams struct {
+	AppName string
+	Name    string
+	Refs    []core.AnnotationKey
+}
+
+// Create takes in an all necessary parameters to generate the IamPolicy name and ensure that the IamPolicy is correlated to the constructs which required its creation.
+func (policy *IamPolicy) Create(dag *core.ResourceGraph, params IamPolicyCreateParams) error {
+	policy.Name = policySanitizer.Apply(fmt.Sprintf("%s-%s", params.AppName, params.Name))
+	policy.ConstructsRef = params.Refs
+	existingPolicy := dag.GetResource(policy.Id())
+	if existingPolicy != nil {
+		return fmt.Errorf("iam policy with name %s already exists", policy.Name)
+	}
+	dag.AddResource(policy)
+	return nil
+}
 func NewPolicyGenerator() *PolicyGenerator {
 	p := &PolicyGenerator{
 		unitsPolicies:       make(map[string][]*IamPolicy),
@@ -259,10 +296,10 @@ func NewIamPolicy(appName string, policyName string, ref core.AnnotationKey, pol
 	}
 }
 
-func NewIamInlinePolicy(policyName string, ref core.AnnotationKey, policy *PolicyDocument) *IamInlinePolicy {
+func NewIamInlinePolicy(policyName string, refs []core.AnnotationKey, policy *PolicyDocument) *IamInlinePolicy {
 	return &IamInlinePolicy{
 		Name:          policySanitizer.Apply(policyName),
-		ConstructsRef: []core.AnnotationKey{ref},
+		ConstructsRef: refs,
 		Policy:        policy,
 	}
 }
diff --git a/pkg/provider/aws/resources/lambda.go b/pkg/provider/aws/resources/lambda.go
index 1f094665..7ca936a7 100644
--- a/pkg/provider/aws/resources/lambda.go
+++ b/pkg/provider/aws/resources/lambda.go
@@ -39,6 +39,70 @@ type (
 	}
 )
 
+type LambdaCreateParams struct {
+	AppName string
+	Refs    []core.AnnotationKey
+	Name    string
+}
+
+func (lambda *LambdaFunction) Create(dag *core.ResourceGraph, params LambdaCreateParams) error {
+
+	name := lambdaFunctionSanitizer.Apply(fmt.Sprintf("%s-%s", params.AppName, params.Name))
+	lambda.Name = name
+	lambda.ConstructsRef = params.Refs
+
+	existingLambda := dag.GetResourceByVertexId(lambda.Id().String())
+	if existingLambda != nil {
+		return fmt.Errorf("lambda with name %s already exists", name)
+	}
+
+	logGroup, err := core.CreateResource[*LogGroup](dag, params)
+	if err != nil {
+		return err
+	}
+	dag.AddDependency(lambda, logGroup)
+	subParams := map[string]any{
+		"Role": RoleCreateParams{
+			AppName: params.AppName,
+			Name:    fmt.Sprintf("%s-ExecutionRole", params.Name),
+			Refs:    lambda.ConstructsRef,
+		},
+		"Image": params,
+	}
+
+	err = dag.CreateDependencies(lambda, subParams)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+type LambdaFunctionConfigureParams struct {
+	Timeout              int
+	MemorySize           int
+	EnvironmentVariables core.EnvironmentVariables
+}
+
+func (lambda *LambdaFunction) Configure(params LambdaFunctionConfigureParams) error {
+	lambda.Timeout = 180
+	lambda.MemorySize = 512
+	if lambda.EnvironmentVariables == nil {
+		lambda.EnvironmentVariables = make(EnvironmentVariables)
+	}
+
+	if params.Timeout != 0 {
+		lambda.Timeout = params.Timeout
+	}
+	if params.MemorySize != 0 {
+		lambda.MemorySize = params.MemorySize
+	}
+	for _, env := range params.EnvironmentVariables {
+		lambda.EnvironmentVariables[env.GetName()] = core.IaCValue{Property: env.GetValue()}
+	}
+
+	return nil
+}
+
 func NewLambdaFunction(unit *core.ExecutionUnit, cfg *config.Application, role *IamRole, image *EcrImage) *LambdaFunction {
 	params := config.ConvertFromInfraParams[config.ServerlessTypeParams](cfg.GetExecutionUnit(unit.ID).InfraParams)
 	return &LambdaFunction{
diff --git a/pkg/provider/aws/resources/rds.go b/pkg/provider/aws/resources/rds.go
index b2be4a9c..fed25287 100644
--- a/pkg/provider/aws/resources/rds.go
+++ b/pkg/provider/aws/resources/rds.go
@@ -107,6 +107,210 @@ type (
 	}
 )
 
+type RdsInstanceCreateParams struct {
+	AppName string
+	Refs    []core.AnnotationKey
+	Name    string
+}
+
+func (instance *RdsInstance) Create(dag *core.ResourceGraph, params RdsInstanceCreateParams) error {
+
+	name := rdsInstanceSanitizer.Apply(fmt.Sprintf("%s-%s", params.AppName, params.Name))
+	instance.Name = name
+	instance.ConstructsRef = params.Refs
+
+	existingInstance := dag.GetResource(instance.Id())
+	if existingInstance != nil {
+		return fmt.Errorf("RdsInstance with name %s already exists", name)
+	}
+
+	instance.SecurityGroups = make([]*SecurityGroup, 1)
+	subParams := map[string]any{
+		"SecurityGroups": []SecurityGroupCreateParams{
+			{
+				AppName: params.AppName,
+				Refs:    params.Refs,
+			},
+		},
+		"SubnetGroup": params,
+	}
+	err := dag.CreateDependencies(instance, subParams)
+	return err
+}
+
+type RdsInstanceConfigureParams struct {
+	DatabaseName      string
+	Username          string
+	Password          string
+	Engine            string
+	EngineVersion     string
+	InstanceClass     string
+	SkipFinalSnapshot bool
+	AllocatedStorage  int
+}
+
+func (instance *RdsInstance) Configure(params RdsInstanceConfigureParams) error {
+	instance.IamDatabaseAuthenticationEnabled = true
+	instance.SkipFinalSnapshot = true
+	instance.DatabaseName = params.DatabaseName
+	instance.Username = generateUsername()
+	instance.Password = generatePassword()
+
+	instance.Engine = "postgres"
+	instance.EngineVersion = "13.7"
+	instance.InstanceClass = "db.t4g.micro"
+	instance.AllocatedStorage = 20
+	credsBytes := []byte(fmt.Sprintf("{\n\"username\": \"%s\",\n\"password\": \"%s\"\n}", instance.Username, instance.Password))
+	credsPath := fmt.Sprintf("secrets/%s", instance.Name)
+	instance.CredentialsFile = &core.RawFile{
+		FPath:   credsPath,
+		Content: credsBytes,
+	}
+	return nil
+}
+
+type RdsSubnetGroupCreateParams struct {
+	AppName string
+	Name    string
+	Refs    []core.AnnotationKey
+}
+
+func (subnetGroup *RdsSubnetGroup) Create(dag *core.ResourceGraph, params RdsSubnetGroupCreateParams) error {
+	subnetGroup.Name = rdsSubnetSanitizer.Apply(fmt.Sprintf("%s-%s", params.AppName, params.Name))
+	subnetGroup.ConstructsRef = params.Refs
+
+	existingSubnetGroup := dag.GetResource(subnetGroup.Id())
+	if existingSubnetGroup != nil {
+		graphSubnetGroup := existingSubnetGroup.(*RdsSubnetGroup)
+		graphSubnetGroup.ConstructsRef = core.DedupeAnnotationKeys(append(graphSubnetGroup.KlothoConstructRef(), params.Refs...))
+		return nil
+	} else {
+		subnetGroup.Subnets = make([]*Subnet, 2)
+		err := dag.CreateDependencies(subnetGroup, map[string]any{
+			"Subnets": []SubnetCreateParams{
+				{
+					AppName: params.AppName,
+					Refs:    params.Refs,
+					AZ:      "0",
+					Type:    PrivateSubnet,
+				},
+				{
+					AppName: params.AppName,
+					Refs:    params.Refs,
+					AZ:      "1",
+					Type:    PrivateSubnet,
+				},
+			},
+		})
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+type RdsProxyCreateParams struct {
+	AppName string
+	Name    string
+	Refs    []core.AnnotationKey
+}
+
+func (proxy *RdsProxy) Create(dag *core.ResourceGraph, params RdsProxyCreateParams) error {
+	proxy.Name = rdsSubnetSanitizer.Apply(fmt.Sprintf("%s-%s", params.AppName, params.Name))
+	proxy.ConstructsRef = params.Refs
+
+	existingProxy := dag.GetResource(proxy.Id())
+	if existingProxy != nil {
+		graphProxy := existingProxy.(*RdsProxy)
+		graphProxy.ConstructsRef = core.DedupeAnnotationKeys(append(graphProxy.KlothoConstructRef(), params.Refs...))
+		return nil
+	} else {
+		proxy.Subnets = make([]*Subnet, 2)
+		proxy.SecurityGroups = make([]*SecurityGroup, 1)
+		err := dag.CreateDependencies(proxy, map[string]any{
+			"Role": RoleCreateParams{
+				AppName: params.AppName,
+				Name:    fmt.Sprintf("%s-ProxyRole", params.Name),
+				Refs:    proxy.ConstructsRef,
+			},
+			"SecurityGroups": []SecurityGroupCreateParams{
+				{
+					AppName: params.AppName,
+					Refs:    params.Refs,
+				},
+			},
+			"Subnets": []SubnetCreateParams{
+				{
+					AppName: params.AppName,
+					Refs:    params.Refs,
+					AZ:      "0",
+					Type:    PrivateSubnet,
+				},
+				{
+					AppName: params.AppName,
+					Refs:    params.Refs,
+					AZ:      "1",
+					Type:    PrivateSubnet,
+				},
+			},
+		})
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+type RdsProxyConfigureParams struct {
+	EngineFamily      string
+	DebugLogging      bool
+	IdleClientTimeout int
+	RequireTls        bool
+}
+
+func (proxy *RdsProxy) Configure(params RdsProxyConfigureParams) error {
+	proxy.DebugLogging = false
+	proxy.EngineFamily = "POSTGRESQL"
+	proxy.IdleClientTimeout = 1800
+	proxy.RequireTls = false
+	return nil
+}
+
+type RdsProxyTargetGroupCreateParams struct {
+	AppName string
+	Name    string
+	Refs    []core.AnnotationKey
+}
+
+func (tg *RdsProxyTargetGroup) Create(dag *core.ResourceGraph, params RdsProxyTargetGroupCreateParams) error {
+
+	tg.Name = rdsProxySanitizer.Apply(fmt.Sprintf("%s-%s", params.AppName, params.Name))
+	tg.ConstructsRef = params.Refs
+	existingTG := dag.GetResource(tg.Id())
+	if existingTG != nil {
+		graphTG := existingTG.(*RdsProxyTargetGroup)
+		graphTG.ConstructsRef = core.DedupeAnnotationKeys(append(graphTG.KlothoConstructRef(), params.Refs...))
+		return nil
+	} else {
+		dag.AddResource(tg)
+	}
+	return nil
+}
+
+type RdsProxyTargetGroupConfigureParams struct {
+	ConnectionPoolConfigurationInfo ConnectionPoolConfigurationInfo
+}
+
+// Configure sets the intristic characteristics of a vpc based on parameters passed in
+func (targetGroup *RdsProxyTargetGroup) Configure(params RdsProxyTargetGroupConfigureParams) error {
+	targetGroup.ConnectionPoolConfigurationInfo = &ConnectionPoolConfigurationInfo{
+		ConnectionBorrowTimeout:   120,
+		MaxConnectionsPercent:     100,
+		MaxIdleConnectionsPercent: 50,
+	}
+	return nil
+}
+
 // CreateRdsInstance takes in an orm construct and creates the necessary resources to support creating a functional RDS Orm implementation
 //
 // If proxy is enabled, a corresponding proxy, secret, and remaining resources will be created.
diff --git a/pkg/provider/aws/resources/region.go b/pkg/provider/aws/resources/region.go
index 125dc966..3c0c8be7 100644
--- a/pkg/provider/aws/resources/region.go
+++ b/pkg/provider/aws/resources/region.go
@@ -4,11 +4,6 @@ import (
 	"github.com/klothoplatform/klotho/pkg/core"
 )
 
-const REGION_TYPE = "region"
-const AVAILABILITY_ZONES_TYPE = "availability_zones"
-const ACCOUNT_ID_TYPE = "account_id"
-const ARN_IAC_VALUE = "arn"
-
 type (
 	Region struct {
 		Name          string
@@ -26,9 +21,19 @@ type (
 	}
 )
 
+const (
+	REGION_NAME             = "region"
+	AVAILABILITY_ZONES_NAME = "AvailabilityZones"
+	ACCOUNT_ID_NAME         = "AccountId"
+	REGION_TYPE             = "region"
+	AVAILABILITY_ZONES_TYPE = "availability_zones"
+	ACCOUNT_ID_TYPE         = "account_id"
+	ARN_IAC_VALUE           = "arn"
+)
+
 func NewRegion() *Region {
 	return &Region{
-		Name:          "region",
+		Name:          REGION_NAME,
 		ConstructsRef: []core.AnnotationKey{},
 	}
 }
@@ -43,13 +48,13 @@ func (region *Region) Id() core.ResourceId {
 	return core.ResourceId{
 		Provider: AWS_PROVIDER,
 		Type:     REGION_TYPE,
-		Name:     region.Name,
+		Name:     REGION_NAME,
 	}
 }
 
 func NewAvailabilityZones() *AvailabilityZones {
 	return &AvailabilityZones{
-		Name:          "AvailabilityZones",
+		Name:          AVAILABILITY_ZONES_NAME,
 		ConstructsRef: []core.AnnotationKey{},
 	}
 }
@@ -64,13 +69,13 @@ func (azs *AvailabilityZones) Id() core.ResourceId {
 	return core.ResourceId{
 		Provider: AWS_PROVIDER,
 		Type:     AVAILABILITY_ZONES_TYPE,
-		Name:     azs.Name,
+		Name:     AVAILABILITY_ZONES_NAME,
 	}
 }
 
 func NewAccountId() *AccountId {
 	return &AccountId{
-		Name:          "AccountId",
+		Name:          ACCOUNT_ID_NAME,
 		ConstructsRef: []core.AnnotationKey{},
 	}
 }
@@ -85,6 +90,6 @@ func (id *AccountId) Id() core.ResourceId {
 	return core.ResourceId{
 		Provider: AWS_PROVIDER,
 		Type:     ACCOUNT_ID_TYPE,
-		Name:     id.Name,
+		Name:     ACCOUNT_ID_NAME,
 	}
 }
diff --git a/pkg/provider/aws/resources/secret.go b/pkg/provider/aws/resources/secret.go
index 72021292..ffbac366 100644
--- a/pkg/provider/aws/resources/secret.go
+++ b/pkg/provider/aws/resources/secret.go
@@ -1,6 +1,8 @@
 package resources
 
 import (
+	"fmt"
+
 	"github.com/klothoplatform/klotho/pkg/core"
 	"github.com/klothoplatform/klotho/pkg/sanitization/aws"
 )
@@ -13,18 +15,74 @@ type (
 	}
 
 	SecretVersion struct {
-		SecretName            string
-		Secret                *Secret
-		Path                  string
-		ConstructsRef         []core.AnnotationKey
-		secretNameUnSanitized string
-		Type                  string
+		SecretName    string
+		Secret        *Secret
+		Path          string
+		ConstructsRef []core.AnnotationKey
+		Name          string
+		Type          string
 	}
 )
 
 const SECRET_TYPE = "secret"
 const SECRET_VERSION_TYPE = "secret_version"
 
+type SecretCreateParams struct {
+	AppName string
+	Refs    []core.AnnotationKey
+	Name    string
+}
+
+// Create takes in an all necessary parameters to generate the Secret name and ensure that the Secret is correlated to the constructs which required its creation.
+func (secret *Secret) Create(dag *core.ResourceGraph, params SecretCreateParams) error {
+	secret.Name = aws.SecretSanitizer.Apply(fmt.Sprintf("%s-%s", params.AppName, params.Name))
+	secret.ConstructsRef = params.Refs
+	existingSecret := dag.GetResource(secret.Id())
+	if existingSecret != nil {
+		return fmt.Errorf("Secret with name %s already exists", secret.Name)
+	}
+	dag.AddResource(secret)
+	return nil
+}
+
+type SecretVersionCreateParams struct {
+	AppName string
+	Refs    []core.AnnotationKey
+	Name    string
+}
+
+// Create takes in an all necessary parameters to generate the SecretVersion name and ensure that the SecretVersion is correlated to the constructs which required its creation.
+//
+// This method will also create dependent resources which are necessary for functionality. Those resources are:
+//   - Secret
+func (sv *SecretVersion) Create(dag *core.ResourceGraph, params SecretVersionCreateParams) error {
+	sv.Name = aws.SecretSanitizer.Apply(fmt.Sprintf("%s-%s", params.AppName, params.Name))
+	sv.ConstructsRef = params.Refs
+	existingSecret := dag.GetResource(sv.Id())
+	if existingSecret != nil {
+		return fmt.Errorf("SecretVersion with name %s already exists", sv.Name)
+	}
+	err := dag.CreateDependencies(sv, map[string]any{
+		"Secret": params,
+	})
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+type SecretVersionConfigureParams struct {
+	Type string
+	Path string
+}
+
+// Configure sets the intristic characteristics of a vpc based on parameters passed in
+func (sv *SecretVersion) Configure(params SecretVersionConfigureParams) error {
+	sv.Type = params.Type
+	sv.Path = params.Path
+	return nil
+}
+
 func NewSecret(annot core.AnnotationKey, secretName string, appName string) *Secret {
 	plainName := appName
 	if secretName != "" {
@@ -39,11 +97,11 @@ func NewSecret(annot core.AnnotationKey, secretName string, appName string) *Sec
 
 func NewSecretVersion(secret *Secret, filePath string) *SecretVersion {
 	return &SecretVersion{
-		SecretName:            secret.SecretName,
-		Secret:                secret,
-		Path:                  filePath,
-		ConstructsRef:         secret.ConstructsRef,
-		secretNameUnSanitized: secret.Name,
+		SecretName:    secret.SecretName,
+		Secret:        secret,
+		Path:          filePath,
+		ConstructsRef: secret.ConstructsRef,
+		Name:          secret.Name,
 	}
 }
 
@@ -67,6 +125,6 @@ func (sv *SecretVersion) Id() core.ResourceId {
 	return core.ResourceId{
 		Provider: AWS_PROVIDER,
 		Type:     SECRET_VERSION_TYPE,
-		Name:     sv.secretNameUnSanitized,
+		Name:     sv.Name,
 	}
 }
diff --git a/pkg/provider/aws/resources/securitygroup.go b/pkg/provider/aws/resources/securitygroup.go
index 3a6a122a..d506b264 100644
--- a/pkg/provider/aws/resources/securitygroup.go
+++ b/pkg/provider/aws/resources/securitygroup.go
@@ -25,6 +25,30 @@ type (
 
 const SG_TYPE = "security_group"
 
+type SecurityGroupCreateParams struct {
+	AppName string
+	Refs    []core.AnnotationKey
+}
+
+func (sg *SecurityGroup) Create(dag *core.ResourceGraph, params SecurityGroupCreateParams) error {
+
+	sg.Name = params.AppName
+	sg.ConstructsRef = params.Refs
+	err := dag.CreateDependencies(sg, map[string]any{
+		"Vpc": params,
+	})
+	if err != nil {
+		return err
+	}
+
+	existingSG := dag.GetResource(sg.Id())
+	if existingSG != nil {
+		graphSG := existingSG.(*SecurityGroup)
+		graphSG.ConstructsRef = core.DedupeAnnotationKeys(append(graphSG.ConstructsRef, params.Refs...))
+	}
+	return nil
+}
+
 // GetSecurityGroup returns the security group if one exists, otherwise creates one, then returns it
 func GetSecurityGroup(cfg *config.Application, dag *core.ResourceGraph) *SecurityGroup {
 	for _, r := range dag.ListResources() {
diff --git a/pkg/provider/aws/resources/vpc.go b/pkg/provider/aws/resources/vpc.go
index 2e763fe8..7d448b1c 100644
--- a/pkg/provider/aws/resources/vpc.go
+++ b/pkg/provider/aws/resources/vpc.go
@@ -88,6 +88,240 @@ type (
 	}
 )
 
+type VpcCreateParams struct {
+	AppName string
+	Refs    []core.AnnotationKey
+}
+
+func (vpc *Vpc) Create(dag *core.ResourceGraph, params VpcCreateParams) error {
+
+	vpc.Name = aws.VpcSanitizer.Apply(params.AppName)
+	vpc.ConstructsRef = params.Refs
+
+	existingVpc := dag.GetResource(vpc.Id())
+	if existingVpc != nil {
+		graphVpc := existingVpc.(*Vpc)
+		graphVpc.ConstructsRef = append(graphVpc.KlothoConstructRef(), params.Refs...)
+	} else {
+		dag.AddResource(vpc)
+	}
+
+	return nil
+}
+
+type VpcConfigureParams struct {
+}
+
+func (vpc *Vpc) Configure(params VpcConfigureParams) error {
+	vpc.CidrBlock = "10.0.0.0/16"
+	vpc.EnableDnsSupport = true
+	vpc.EnableDnsHostnames = true
+	return nil
+}
+
+type EipCreateParams struct {
+	AppName string
+	IpName  string
+	Refs    []core.AnnotationKey
+}
+
+func (eip *ElasticIp) Create(dag *core.ResourceGraph, params EipCreateParams) error {
+	eip.Name = elasticIpSanitizer.Apply(fmt.Sprintf("%s-%s", params.AppName, params.IpName))
+	eip.ConstructsRef = params.Refs
+	existingEip := dag.GetResourceByVertexId(eip.Id().String())
+	if existingEip != nil {
+		graphEip := existingEip.(*ElasticIp)
+		graphEip.ConstructsRef = append(graphEip.ConstructsRef, params.Refs...)
+	} else {
+		dag.AddResource(eip)
+	}
+	return nil
+}
+
+type IgwCreateParams struct {
+	AppName string
+	Refs    []core.AnnotationKey
+}
+
+func (igw *InternetGateway) Create(dag *core.ResourceGraph, params IgwCreateParams) error {
+
+	igw.Name = igwSanitizer.Apply(fmt.Sprintf("%s-igw", params.AppName))
+	igw.ConstructsRef = params.Refs
+
+	existingIgw := dag.GetResourceByVertexId(igw.Id().String())
+
+	if existingIgw != nil {
+		graphIgw := existingIgw.(*InternetGateway)
+		graphIgw.ConstructsRef = append(graphIgw.ConstructsRef, params.Refs...)
+	} else {
+		err := dag.CreateDependencies(igw, map[string]any{
+			"Vpc": params,
+		})
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+type NatCreateParams struct {
+	AppName string
+	Refs    []core.AnnotationKey
+	AZ      string
+}
+
+func (nat *NatGateway) Create(dag *core.ResourceGraph, params NatCreateParams) error {
+
+	nat.Name = natGatewaySanitizer.Apply(fmt.Sprintf("%s-%s", params.AppName, params.AZ))
+	nat.ConstructsRef = params.Refs
+
+	existingNat := dag.GetResourceByVertexId(nat.Id().String())
+	if existingNat != nil {
+		graphNat := existingNat.(*NatGateway)
+		graphNat.ConstructsRef = append(graphNat.ConstructsRef, params.Refs...)
+	} else {
+		subResourceParams := map[string]any{
+			"Subnet": SubnetCreateParams{
+				AppName: params.AppName,
+				Refs:    params.Refs,
+				AZ:      params.AZ,
+				Type:    PublicSubnet,
+			},
+			"ElasticIp": EipCreateParams{
+				AppName: params.AppName,
+				Refs:    params.Refs,
+				IpName:  params.AZ,
+			},
+		}
+		err := dag.CreateDependencies(nat, subResourceParams)
+		return err
+	}
+	return nil
+}
+
+type SubnetCreateParams struct {
+	AppName string
+	Refs    []core.AnnotationKey
+	AZ      string
+	Type    string
+}
+
+func (subnet *Subnet) Create(dag *core.ResourceGraph, params SubnetCreateParams) error {
+	subnet.Name = subnetSanitizer.Apply(fmt.Sprintf("%s-%s%s", params.AppName, params.Type, params.AZ))
+	subnet.ConstructsRef = params.Refs
+	subnet.AvailabilityZone = core.IaCValue{Resource: NewAvailabilityZones(), Property: params.AZ}
+	subnet.Type = params.Type
+
+	routeTableParams := RouteTableCreateParams{
+		AppName: params.AppName,
+		Refs:    params.Refs,
+	}
+	if subnet.Type == PrivateSubnet {
+		routeTableParams.Name = fmt.Sprintf("%s%s", params.Type, params.AZ)
+	} else {
+		routeTableParams.Name = fmt.Sprintf(params.Type)
+	}
+	rt := &RouteTable{}
+	err := rt.Create(dag, routeTableParams)
+	if err != nil {
+		return err
+	}
+	if subnet.Type == PrivateSubnet {
+		nat := &NatGateway{}
+		natParams := NatCreateParams{
+			AppName: params.AppName,
+			Refs:    params.Refs,
+			AZ:      params.AZ,
+		}
+		err := nat.Create(dag, natParams)
+		if err != nil {
+			return err
+		}
+		dag.AddDependency(rt, nat)
+	} else if subnet.Type == PublicSubnet {
+		igw := &InternetGateway{}
+		igwParams := IgwCreateParams{
+			AppName: params.AppName,
+			Refs:    params.Refs,
+		}
+		err := igw.Create(dag, igwParams)
+		if err != nil {
+			return err
+		}
+		dag.AddDependency(rt, igw)
+	}
+
+	err = dag.CreateDependencies(subnet, map[string]any{
+		"Vpc": params,
+	})
+	if err != nil {
+		return err
+	}
+	dag.AddDependency(subnet, NewAvailabilityZones())
+	dag.AddDependency(rt, subnet)
+
+	// We must check to see if there is an existent subnet after calling create dependencies because the id of the subnet has a namespace based on the vpc
+	existingSubnet := dag.GetResourceByVertexId(subnet.Id().String())
+	if existingSubnet != nil {
+		graphSubnet := existingSubnet.(*Subnet)
+		graphSubnet.ConstructsRef = core.DedupeAnnotationKeys(append(graphSubnet.ConstructsRef, params.Refs...))
+	}
+	return nil
+}
+
+type SubnetConfigureParams struct {
+}
+
+func (subnet *Subnet) Configure(params SubnetConfigureParams) error {
+	if subnet.Type == PrivateSubnet {
+		if subnet.AvailabilityZone.Property == "0" {
+			subnet.CidrBlock = "10.0.0.0/18"
+		} else if subnet.AvailabilityZone.Property == "1" {
+			subnet.CidrBlock = "10.0.64.0/18"
+		}
+	} else if subnet.Type == PublicSubnet {
+		if subnet.AvailabilityZone.Property == "0" {
+			subnet.CidrBlock = "10.0.128.0/18"
+		} else if subnet.AvailabilityZone.Property == "1" {
+			subnet.CidrBlock = "10.0.192.0/18"
+
+		}
+		subnet.MapPublicIpOnLaunch = true
+	}
+	return nil
+}
+
+type RouteTableCreateParams struct {
+	AppName string
+	Name    string
+	Refs    []core.AnnotationKey
+}
+
+func (rt *RouteTable) Create(dag *core.ResourceGraph, params RouteTableCreateParams) error {
+
+	rt.Name = subnetSanitizer.Apply(fmt.Sprintf("%s-%s", params.AppName, params.Name))
+
+	subParams := map[string]any{
+		"Vpc": VpcCreateParams{
+			AppName: params.AppName,
+			Refs:    params.Refs,
+		},
+	}
+	err := dag.CreateDependencies(rt, subParams)
+	if err != nil {
+		return err
+	}
+	dag.AddDependenciesReflect(rt)
+
+	// We must check to see if there is an existent route table after calling create dependencies because the id of the subnet can contain a namespace based on the vpc
+	existingRt := dag.GetResourceByVertexId(rt.Id().String())
+	if existingRt != nil {
+		graphRt := existingRt.(*RouteTable)
+		graphRt.ConstructsRef = core.DedupeAnnotationKeys(append(graphRt.ConstructsRef, params.Refs...))
+	}
+	return nil
+}
+
 // CreateNetwork takes in a config and uses the appName to create an aws network and inject it into the dag.
 //
 // The network consists of:
diff --git a/pkg/provider/aws/secret.go b/pkg/provider/aws/secret.go
index 3b56dfd0..fdcb1503 100644
--- a/pkg/provider/aws/secret.go
+++ b/pkg/provider/aws/secret.go
@@ -36,7 +36,7 @@ func (a *AWS) generateSecret(construct core.Construct, result *core.ConstructGra
 			Property: resources.ARN_IAC_VALUE,
 		}}
 		policyDoc := resources.CreateAllowPolicyDocument(actions, policyResources)
-		policy := resources.NewIamInlinePolicy(fmt.Sprintf("%s-secretsmanager", secret.Name), construct.Provenance(), policyDoc)
+		policy := resources.NewIamInlinePolicy(fmt.Sprintf("%s-secretsmanager", secret.Name), []core.AnnotationKey{construct.Provenance()}, policyDoc)
 		a.PolicyGenerator.AddInlinePolicyToUnit(unit.Id(), policy)
 	}
 	return nil


Test patch:
diff --git a/pkg/core/construct_graph_test.go b/pkg/core/construct_graph_test.go
index 29f267e3..77be27a8 100644
--- a/pkg/core/construct_graph_test.go
+++ b/pkg/core/construct_graph_test.go
@@ -144,7 +144,7 @@ func Test_GetDownstreamDependencies(t *testing.T) {
 			g.AddVertex(tt.construct)
 			for _, c := range tt.deps {
 				g.AddVertex(c)
-				g.AddEdge(tt.construct.Id(), c.Id())
+				g.AddEdge(tt.construct.Id(), c.Id(), nil)
 			}
 			deps := constructGraph.GetDownstreamDependencies(tt.construct)
 			if tt.want != nil && !assert.NotNil(deps) {
@@ -214,7 +214,7 @@ func Test_GetUpstreamDependencies(t *testing.T) {
 			g.AddVertex(tt.construct)
 			for _, c := range tt.deps {
 				g.AddVertex(c)
-				g.AddEdge(c.Id(), tt.construct.Id())
+				g.AddEdge(c.Id(), tt.construct.Id(), nil)
 			}
 			deps := constructGraph.GetUpstreamDependencies(tt.construct)
 			if tt.want != nil && !assert.NotNil(deps) {
diff --git a/pkg/core/resource_graph_test.go b/pkg/core/resource_graph_test.go
index 62f35753..f8bf6c53 100644
--- a/pkg/core/resource_graph_test.go
+++ b/pkg/core/resource_graph_test.go
@@ -3,13 +3,15 @@ package core
 import (
 	"testing"
 
+	"github.com/google/uuid"
 	"github.com/stretchr/testify/assert"
 )
 
 type NestedResources struct {
-	Resource      Resource
-	ResourceArray []Resource
-	ResourceMap   map[string]Resource
+	Resource           Resource
+	SpecificDependency *testResource
+	ResourceArray      []Resource
+	ResourceMap        map[string]Resource
 }
 
 type testResource struct {
@@ -39,6 +41,16 @@ func (tr *testResource) KlothoConstructRef() []AnnotationKey {
 	return nil
 }
 
+type testResourceParams struct {
+	Name string
+}
+
+func (tr *testResource) Create(dag *ResourceGraph, metadata testResourceParams) error {
+	currName := metadata.Name + uuid.NewString()
+	*tr = testResource{Name: currName}
+	return nil
+}
+
 // Id returns the id of the cloud resource
 func (tr *testResource) Id() ResourceId {
 	return ResourceId{
@@ -121,3 +133,127 @@ func TestResourceGraph_AddDependenciesReflect(t *testing.T) {
 	}
 	assert.Nil(dag.GetDependencyByVertexIds(tr.Id().String(), (&testResource{Name: "nested_single"}).Id().String()))
 }
+
+func TestResourceGraph_CreateDependencies(t *testing.T) {
+	tr := &testResource{
+		Name: "id",
+
+		NestedResources: NestedResources{
+			Resource:      &testResource{Name: "nested"},
+			ResourceArray: []Resource{&testResource{Name: "nested_arr1"}, &testResource{Name: "nested_arr2"}},
+			ResourceMap: map[string]Resource{
+				"one": &testResource{Name: "nested_map1"},
+				"two": &testResource{Name: "nested_map2"},
+			},
+		},
+
+		SingleDependency: &testResource{Name: "single", NestedResources: NestedResources{Resource: &testResource{Name: "nested_single"}}},
+
+		DependencyArray:  []Resource{&testResource{Name: "arr1"}, &testResource{Name: "arr2"}},
+		SpecificDepArray: []*testResource{{Name: "spec_arr1"}, {Name: "spec_arr2"}},
+
+		DependencyMap: map[string]Resource{
+			"one": &testResource{Name: "map1"},
+			"two": &testResource{Name: "map2"},
+		},
+		SpecificDepMap: map[string]*testResource{
+			"one": {Name: "spec_map1"},
+			"two": {Name: "spec_map2"},
+		},
+
+		IacValue:    IaCValue{Resource: &testResource{Name: "value1"}},
+		IacValuePtr: &IaCValue{Resource: &testResource{Name: "value2"}},
+		IacValueArr: []IaCValue{
+			{Resource: &testResource{Name: "value_arr1"}},
+			{Resource: &testResource{Name: "value_arr2"}},
+		},
+		IacValuePtrArr: []*IaCValue{
+			{Resource: &testResource{Name: "value_ptr_arr1"}},
+			{Resource: &testResource{Name: "value_ptr_arr2"}},
+		},
+		IacValueMap: map[string]IaCValue{
+			"one": {Resource: &testResource{Name: "value_map1"}},
+			"two": {Resource: &testResource{Name: "value_map2"}},
+		},
+		IacValuePtrMap: map[string]*IaCValue{
+			"one": {Resource: &testResource{Name: "value_ptr_map1"}},
+			"two": {Resource: &testResource{Name: "value_ptr_map2"}},
+		},
+	}
+
+	subParams := map[string]any{
+		"NestedResources": map[string]any{
+			"Resource":           testResourceParams{Name: "nested"},
+			"SpecificDependency": testResourceParams{Name: "nested_specific"},
+			"ResourceArray":      []testResourceParams{{Name: "nested_arr1"}, {Name: "nested_arr2"}},
+			"ResourceMap": map[string]testResourceParams{
+				"one": {Name: "nested_map1"},
+				"two": {Name: "nested_map2"},
+			},
+		},
+
+		"SingleDependency":   testResourceParams{Name: "single"},
+		"SpecificDependency": testResourceParams{Name: "specific"},
+		"DependencyArray":    []testResourceParams{{Name: "arr1"}, {Name: "arr2"}},
+		"SpecificDepArray":   []testResourceParams{{Name: "spec_arr1"}, {Name: "spec_arr2"}},
+
+		"DependencyMap": map[string]testResourceParams{
+			"one": {Name: "map1"},
+			"two": {Name: "map2"},
+		},
+		"SpecificDepMap": map[string]testResourceParams{
+			"one": {Name: "spec_map1"},
+			"two": {Name: "spec_map2"},
+		},
+
+		"IacValue":    testResourceParams{Name: "value1"},
+		"IacValuePtr": testResourceParams{Name: "value2"},
+		"IacValueArr": []testResourceParams{
+			{Name: "value_arr1"},
+			{Name: "value_arr2"},
+		},
+		"IacValuePtrArr": []testResourceParams{
+			{Name: "value_ptr_arr1"},
+			{Name: "value_ptr_arr2"},
+		},
+		"IacValueMap": map[string]testResourceParams{
+			"one": {Name: "value_map1"},
+			"two": {Name: "value_map2"},
+		},
+		"IacValuePtrMap": map[string]testResourceParams{
+			"one": {Name: "value_ptr_map1"},
+			"two": {Name: "value_ptr_map2"},
+		},
+	}
+
+	dag := NewResourceGraph()
+
+	err := dag.CreateDependencies(tr, subParams)
+
+	assert := assert.New(t)
+
+	if !assert.NoError(err) {
+		return
+	}
+	assert.Len(dag.ListResources(), 27)
+	assert.Len(dag.ListDependencies(), 26)
+	assert.NotNil(tr.SpecificDependency)
+	assert.NotNil(tr.NestedResources.SpecificDependency)
+	for _, target := range []string{
+		"single", "single_specific",
+		"arr1", "arr2",
+		"spec_arr1", "spec_arr2",
+		"map1", "map2",
+		"spec_map1", "spec_map2",
+		"value1", "value2",
+		"value_arr1", "value_arr2",
+		"value_ptr_arr1", "value_ptr_arr2",
+		"value_map1", "value_map2",
+		"value_ptr_map1", "value_ptr_map2",
+		"nested",
+		"nested_arr1", "nested_arr2",
+		"nested_map1", "nested_map2",
+	} {
+		assert.Nil(dag.GetDependencyByVertexIds(tr.Id().String(), (&testResource{Name: target}).Id().String()), "source -> %s", target)
+	}
+}
diff --git a/pkg/graph/graph_test.go b/pkg/graph/graph_test.go
index 51da4fcb..c0070e12 100644
--- a/pkg/graph/graph_test.go
+++ b/pkg/graph/graph_test.go
@@ -20,8 +20,8 @@ func TestSimpleGraph(t *testing.T) {
 	d.AddVertex(a)
 	d.AddVertex(b)
 	d.AddVertex(c)
-	d.AddEdge(a.Id(), b.Id())
-	d.AddEdge(a.Id(), c.Id())
+	d.AddEdge(a.Id(), b.Id(), nil)
+	d.AddEdge(a.Id(), c.Id(), nil)
 
 	test(t, "roots", func(assert *assert.Assertions) {
 		assert.Equal([]DummyVertex{a}, d.Roots())
@@ -50,7 +50,7 @@ func TestCycleToSelf(t *testing.T) {
 	d := NewDirected(DummyVertex.Id)
 	v := DummyVertex("dummy")
 	d.AddVertex(v)
-	d.AddEdge(v.Id(), v.Id())
+	d.AddEdge(v.Id(), v.Id(), nil)
 	assert.Equal(
 		[]Edge[DummyVertex]{
 			{
@@ -68,8 +68,8 @@ func TestCycle(t *testing.T) {
 	v2 := DummyVertex("world")
 	d.AddVertex(v1)
 	d.AddVertex(v2)
-	d.AddEdge(v1.Id(), v2.Id())
-	d.AddEdge(v2.Id(), v1.Id())
+	d.AddEdge(v1.Id(), v2.Id(), nil)
+	d.AddEdge(v2.Id(), v1.Id(), nil)
 	assert.Equal(
 		[]Edge[DummyVertex]{
 			{
@@ -102,8 +102,8 @@ func TestNegativeCases(t *testing.T) {
 		v2 := DummyVertex("world")
 		d.AddVertex(v1)
 		d.AddVertex(v2)
-		d.AddEdge(v1.Id(), v2.Id())
-		d.AddEdge(v1.Id(), v2.Id())
+		d.AddEdge(v1.Id(), v2.Id(), nil)
+		d.AddEdge(v1.Id(), v2.Id(), nil)
 		assert.Equal(
 			[]Edge[DummyVertex]{
 				{
diff --git a/pkg/infra/iac2/templates_compiler_test.go b/pkg/infra/iac2/templates_compiler_test.go
index f5e25f05..448e99bd 100644
--- a/pkg/infra/iac2/templates_compiler_test.go
+++ b/pkg/infra/iac2/templates_compiler_test.go
@@ -322,7 +322,7 @@ func Test_handleIaCValue(t *testing.T) {
 			resourceVarNamesById: map[core.ResourceId]string{
 				{Provider: "aws", Type: "availability_zones", Name: "AvailabilityZones"}: "azs",
 			},
-			want: "availabilityZones.names[2]",
+			want: "azs.names[2]",
 		},
 		{
 			name: "nlb uri",
diff --git a/pkg/knowledge_base/edge_kb_test.go b/pkg/knowledge_base/edge_kb_test.go
new file mode 100644
index 00000000..43c10785
--- /dev/null
+++ b/pkg/knowledge_base/edge_kb_test.go
@@ -0,0 +1,227 @@
+package knowledgebase
+
+import (
+	"reflect"
+	"testing"
+
+	"github.com/klothoplatform/klotho/pkg/core"
+
+	klothograph "github.com/klothoplatform/klotho/pkg/graph"
+
+	"github.com/dominikbraun/graph"
+	"github.com/stretchr/testify/assert"
+)
+
+var TestKnowledgeBase = &EdgeKB{
+	Edge{Source: reflect.TypeOf(&A{}), Destination: reflect.TypeOf(&B{})}: EdgeDetails{
+		ExpansionFunc: func(source, dest core.Resource, dag *core.ResourceGraph, data EdgeData) error {
+			b := dest.(*B)
+			b.Name = "B"
+			dag.AddDependency(source, dest)
+			return nil
+		},
+		Configure: func(source, dest core.Resource, dag *core.ResourceGraph, data EdgeData) error {
+			a := source.(*A)
+			a.Name = "name"
+			return nil
+		},
+	},
+	Edge{Source: reflect.TypeOf(&A{}), Destination: reflect.TypeOf(&E{})}: EdgeDetails{
+		ExpansionFunc: func(source, dest core.Resource, dag *core.ResourceGraph, data EdgeData) error {
+			dag.AddDependency(source, dest)
+			return nil
+		},
+		ValidDestinations: []reflect.Type{typeE},
+	},
+	Edge{Source: reflect.TypeOf(&B{}), Destination: reflect.TypeOf(&C{})}: EdgeDetails{
+		ExpansionFunc: func(source, dest core.Resource, dag *core.ResourceGraph, data EdgeData) error {
+			dag.AddDependency(source, dest)
+			return nil
+		},
+	},
+	Edge{Source: reflect.TypeOf(&C{}), Destination: reflect.TypeOf(&D{})}: EdgeDetails{
+		ExpansionFunc: func(source, dest core.Resource, dag *core.ResourceGraph, data EdgeData) error {
+			dag.AddDependency(source, dest)
+			return nil
+		},
+		ValidDestinations: []reflect.Type{typeE, typeD},
+	},
+	Edge{Source: reflect.TypeOf(&C{}), Destination: reflect.TypeOf(&E{})}: EdgeDetails{
+		ExpansionFunc: func(source, dest core.Resource, dag *core.ResourceGraph, data EdgeData) error {
+			dag.AddDependency(source, dest)
+			return nil
+		},
+		ValidDestinations: []reflect.Type{typeE},
+	},
+	Edge{Source: reflect.TypeOf(&D{}), Destination: reflect.TypeOf(&B{})}: EdgeDetails{
+		ExpansionFunc: func(source, dest core.Resource, dag *core.ResourceGraph, data EdgeData) error {
+			dag.AddDependency(source, dest)
+			return nil
+		},
+		ValidDestinations: []reflect.Type{typeC},
+	},
+	Edge{Source: reflect.TypeOf(&D{}), Destination: reflect.TypeOf(&E{})}: EdgeDetails{
+		ExpansionFunc: func(source, dest core.Resource, dag *core.ResourceGraph, data EdgeData) error {
+			dag.AddDependency(source, dest)
+			return nil
+		},
+		ValidDestinations: []reflect.Type{typeE},
+	},
+}
+
+var typeA = reflect.TypeOf(&A{})
+var typeB = reflect.TypeOf(&B{})
+var typeC = reflect.TypeOf(&C{})
+var typeD = reflect.TypeOf(&D{})
+var typeE = reflect.TypeOf(&E{})
+
+func Test_FindPaths(t *testing.T) {
+	cases := []struct {
+		name   string
+		source core.Resource
+		dest   core.Resource
+		want   []Path
+	}{
+		{
+			name:   "paths from a",
+			source: &A{},
+			dest:   &E{},
+			want: []Path{
+				{{typeA, typeB}, {typeB, typeC}, {typeC, typeD}, {typeD, typeE}},
+				{{typeA, typeB}, {typeB, typeC}, {typeC, typeE}},
+				{{typeA, typeE}},
+			},
+		},
+		{
+			name:   "paths from b",
+			source: &B{},
+			dest:   &E{},
+			want: []Path{
+				{{typeB, typeC}, {typeC, typeD}, {typeD, typeE}},
+				{{typeB, typeC}, {typeC, typeE}},
+				{{typeA, typeB}, {typeA, typeE}},
+			},
+		},
+		{
+			name:   "paths from d to c",
+			source: &D{},
+			dest:   &C{},
+			want: []Path{
+				{{typeD, typeB}, {typeB, typeC}},
+			},
+		},
+		{
+			name:   "no paths from e",
+			source: &E{},
+			dest:   &A{},
+			want:   []Path{},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+
+			result := TestKnowledgeBase.FindPaths(reflect.TypeOf(tt.source), reflect.TypeOf(tt.dest))
+			assert.ElementsMatch(tt.want, result)
+		})
+	}
+}
+
+func Test_ConfigureFromEdgeData(t *testing.T) {
+	cases := []struct {
+		name   string
+		source core.Resource
+		dest   core.Resource
+		data   EdgeData
+		want   []klothograph.Edge[core.Resource]
+	}{
+		{
+			name:   "node must and must not exist",
+			source: &A{},
+			dest:   &B{},
+			want: []klothograph.Edge[core.Resource]{
+				{Source: &A{Name: "name"}, Destination: &B{}, Properties: graph.EdgeProperties{Attributes: map[string]string{}, Data: EdgeData{}}},
+			},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+			dag.AddDependencyWithData(tt.source, tt.dest, tt.data)
+			err := TestKnowledgeBase.ConfigureFromEdgeData(dag)
+			assert.NoError(err)
+			assert.ElementsMatch(tt.want, dag.ListDependencies())
+		})
+	}
+}
+
+func Test_ExpandEdges(t *testing.T) {
+	cases := []struct {
+		name   string
+		source core.Resource
+		dest   core.Resource
+		data   EdgeData
+		want   []klothograph.Edge[core.Resource]
+	}{
+		{
+			name:   "node must and must not exist",
+			source: &A{Name: "A"},
+			dest:   &E{Name: "E"},
+			data: EdgeData{
+				Constraint: EdgeConstraint{
+					NodeMustExist:    []core.Resource{&C{}},
+					NodeMustNotExist: []core.Resource{&D{}},
+				},
+			},
+			want: []klothograph.Edge[core.Resource]{
+				{Source: &A{Name: "A"}, Destination: &B{Name: "B"}, Properties: graph.EdgeProperties{Attributes: map[string]string{}}},
+				{Source: &B{Name: "B"}, Destination: &C{}, Properties: graph.EdgeProperties{Attributes: map[string]string{}}},
+				{Source: &C{}, Destination: &E{Name: "E"}, Properties: graph.EdgeProperties{Attributes: map[string]string{}}},
+			},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+			dag.AddDependencyWithData(tt.source, tt.dest, tt.data)
+			err := TestKnowledgeBase.ExpandEdges(dag)
+			assert.NoError(err)
+			assert.ElementsMatch(tt.want, dag.ListDependencies())
+		})
+	}
+}
+
+type (
+	A struct {
+		Name string
+	}
+	B struct {
+		Name string
+	}
+	C struct {
+		Name string
+	}
+	D struct {
+		Name string
+	}
+	E struct {
+		Name string
+	}
+)
+
+func (f *A) Id() core.ResourceId                      { return core.ResourceId{Name: "A" + f.Name} }
+func (f *A) KlothoConstructRef() []core.AnnotationKey { return nil }
+
+func (b B) Id() core.ResourceId                      { return core.ResourceId{Name: "B" + b.Name} }
+func (f B) KlothoConstructRef() []core.AnnotationKey { return nil }
+
+func (p *C) Id() core.ResourceId                      { return core.ResourceId{Name: "C" + p.Name} }
+func (f *C) KlothoConstructRef() []core.AnnotationKey { return nil }
+
+func (p *D) Id() core.ResourceId                      { return core.ResourceId{Name: "D" + p.Name} }
+func (f *D) KlothoConstructRef() []core.AnnotationKey { return nil }
+
+func (p *E) Id() core.ResourceId                      { return core.ResourceId{Name: "E" + p.Name} }
+func (f *E) KlothoConstructRef() []core.AnnotationKey { return nil }
diff --git a/pkg/provider/aws/execution_unit_test.go b/pkg/provider/aws/execution_unit_test.go
index f43be46e..81b8caee 100644
--- a/pkg/provider/aws/execution_unit_test.go
+++ b/pkg/provider/aws/execution_unit_test.go
@@ -1,6 +1,7 @@
 package aws
 
 import (
+	"fmt"
 	"testing"
 
 	"github.com/klothoplatform/klotho/pkg/annotation"
@@ -13,6 +14,60 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
+func Test_ExpandExecutionUnit(t *testing.T) {
+	eu := &core.ExecutionUnit{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}, DockerfilePath: "path"}
+	cases := []struct {
+		name   string
+		unit   *core.ExecutionUnit
+		chart  *kubernetes.HelmChart
+		config *config.Application
+		want   coretesting.ResourcesExpectation
+	}{
+		{
+			name:   "single lambda exec unit",
+			unit:   eu,
+			config: &config.Application{AppName: "my-app", Defaults: config.Defaults{ExecutionUnit: config.KindDefaults{Type: Lambda}}},
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:ecr_image:my-app-test",
+					"aws:ecr_repo:my-app",
+					"aws:iam_role:my-app-test-ExecutionRole",
+					"aws:lambda_function:my-app-test",
+					"aws:log_group:my-app-test",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:ecr_image:my-app-test", Destination: "aws:ecr_repo:my-app"},
+					{Source: "aws:lambda_function:my-app-test", Destination: "aws:ecr_image:my-app-test"},
+					{Source: "aws:lambda_function:my-app-test", Destination: "aws:iam_role:my-app-test-ExecutionRole"},
+					{Source: "aws:lambda_function:my-app-test", Destination: "aws:log_group:my-app-test"},
+				},
+			},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+			if tt.chart != nil {
+				dag.AddResource(tt.chart)
+			}
+
+			aws := AWS{
+				Config: tt.config,
+			}
+			err := aws.expandExecutionUnit(dag, tt.unit)
+
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+			fmt.Println(coretesting.ResoucesFromDAG(dag).GoString())
+			resource := aws.GetResourceTiedToConstruct(tt.unit)
+			assert.NotNil(resource)
+		})
+	}
+}
+
 func Test_GenerateExecUnitResources(t *testing.T) {
 	unit := &core.ExecutionUnit{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}}
 	fs := &core.Fs{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}}
diff --git a/pkg/provider/aws/knowledgebase/edge_configure_test.go b/pkg/provider/aws/knowledgebase/edge_configure_test.go
new file mode 100644
index 00000000..f5b7bc8f
--- /dev/null
+++ b/pkg/provider/aws/knowledgebase/edge_configure_test.go
@@ -0,0 +1,181 @@
+package knowledgebase
+
+import (
+	"testing"
+
+	dgraph "github.com/dominikbraun/graph"
+	"github.com/klothoplatform/klotho/pkg/annotation"
+	"github.com/klothoplatform/klotho/pkg/core"
+	"github.com/klothoplatform/klotho/pkg/graph"
+	knowledgebase "github.com/klothoplatform/klotho/pkg/knowledge_base"
+	"github.com/klothoplatform/klotho/pkg/provider/aws/resources"
+	"github.com/stretchr/testify/assert"
+)
+
+func Test_ConfigureEdge(t *testing.T) {
+	orm := &core.Orm{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}}
+	cases := []struct {
+		name string
+		edge []graph.Edge[core.Resource]
+		want []core.Resource
+	}{
+		{
+			name: "single rds lambda",
+			edge: []graph.Edge[core.Resource]{
+				{
+					Source:      &resources.LambdaFunction{Name: "lambda", Subnets: []*resources.Subnet{{Name: "sub1"}}, Role: &resources.IamRole{}, EnvironmentVariables: make(resources.EnvironmentVariables)},
+					Destination: &resources.RdsInstance{Name: "rds"},
+					Properties: dgraph.EdgeProperties{
+						Data: knowledgebase.EdgeData{
+							AppName:              "my-app",
+							Source:               &resources.LambdaFunction{Name: "lambda"},
+							Destination:          &resources.RdsInstance{Name: "rds"},
+							EnvironmentVariables: []core.EnvironmentVariable{core.GenerateOrmConnStringEnvVar(orm)},
+						},
+					},
+				},
+			},
+			want: []core.Resource{&resources.LambdaFunction{
+				Name:    "lambda",
+				Subnets: []*resources.Subnet{{Name: "sub1"}},
+				Role: &resources.IamRole{
+					InlinePolicies: []*resources.IamInlinePolicy{
+						{
+							Name:          "rds-connectionpolicy",
+							ConstructsRef: []core.AnnotationKey{},
+							Policy: &resources.PolicyDocument{
+								Version: "2012-10-17",
+								Statement: []resources.StatementEntry{
+									{
+										Effect:   "Allow",
+										Action:   []string{"rds-db:connect"},
+										Resource: []core.IaCValue{{Resource: &resources.RdsInstance{Name: "rds"}, Property: resources.RDS_CONNECTION_ARN_IAC_VALUE}},
+									},
+								},
+							},
+						},
+					},
+				},
+				EnvironmentVariables: resources.EnvironmentVariables{"TEST_PERSIST_ORM_CONNECTION": core.IaCValue{Resource: &resources.RdsInstance{Name: "rds"}, Property: string(core.CONNECTION_STRING)}},
+			},
+				&resources.RdsInstance{Name: "rds"},
+			},
+		},
+		{
+			name: "single rds proxy and lambda",
+			edge: []graph.Edge[core.Resource]{
+				{
+					Source:      &resources.LambdaFunction{Name: "lambda", Subnets: []*resources.Subnet{{Name: "sub1"}}, Role: &resources.IamRole{}, EnvironmentVariables: make(resources.EnvironmentVariables)},
+					Destination: &resources.RdsProxy{Name: "rds", Role: &resources.IamRole{Name: "ProxyRole"}, Auths: []*resources.ProxyAuth{{SecretArn: core.IaCValue{Resource: &resources.Secret{Name: "Secret"}}}}},
+					Properties: dgraph.EdgeProperties{
+						Data: knowledgebase.EdgeData{
+							AppName:              "my-app",
+							Source:               &resources.LambdaFunction{Name: "lambda"},
+							Destination:          &resources.RdsInstance{Name: "rds"},
+							EnvironmentVariables: []core.EnvironmentVariable{core.GenerateOrmConnStringEnvVar(orm)},
+						},
+					},
+				},
+				{
+					Source:      &resources.RdsProxyTargetGroup{Name: "rds"},
+					Destination: &resources.RdsProxy{Name: "rds", Role: &resources.IamRole{Name: "ProxyRole"}, Auths: []*resources.ProxyAuth{{SecretArn: core.IaCValue{Resource: &resources.Secret{Name: "Secret"}}}}},
+					Properties: dgraph.EdgeProperties{
+						Data: knowledgebase.EdgeData{
+							AppName:              "my-app",
+							Source:               &resources.LambdaFunction{Name: "lambda"},
+							Destination:          &resources.RdsInstance{Name: "rds"},
+							EnvironmentVariables: []core.EnvironmentVariable{core.GenerateOrmConnStringEnvVar(orm)},
+						},
+					},
+				},
+				{
+					Source:      &resources.RdsProxy{Name: "rds"},
+					Destination: &resources.Secret{Name: "Secret"},
+				},
+				{
+					Source:      &resources.Secret{Name: "Secret"},
+					Destination: &resources.SecretVersion{Name: "sv"},
+				},
+				{
+					Source:      &resources.RdsProxy{Name: "rds"},
+					Destination: &resources.Secret{Name: "Secret"},
+				},
+				{
+					Source: &resources.RdsProxyTargetGroup{
+						Name: "rds",
+						RdsProxy: &resources.RdsProxy{
+							Name:  "rds",
+							Auths: []*resources.ProxyAuth{{SecretArn: core.IaCValue{Resource: &resources.Secret{Name: "Secret"}}}},
+						},
+					},
+					Destination: &resources.RdsInstance{Name: "instance", CredentialsFile: &core.FileRef{FPath: "rds"}},
+					Properties: dgraph.EdgeProperties{
+						Data: knowledgebase.EdgeData{
+							AppName:              "my-app",
+							Source:               &resources.LambdaFunction{Name: "lambda"},
+							Destination:          &resources.RdsInstance{Name: "rds"},
+							EnvironmentVariables: []core.EnvironmentVariable{core.GenerateOrmConnStringEnvVar(orm)},
+						},
+					},
+				},
+			},
+			want: []core.Resource{
+				&resources.LambdaFunction{
+					Name:    "lambda",
+					Subnets: []*resources.Subnet{{Name: "sub1"}},
+					Role: &resources.IamRole{
+						InlinePolicies: []*resources.IamInlinePolicy{
+							{
+								Name:          "instance-connectionpolicy",
+								ConstructsRef: []core.AnnotationKey{},
+								Policy: &resources.PolicyDocument{
+									Version: "2012-10-17",
+									Statement: []resources.StatementEntry{
+										{
+											Effect:   "Allow",
+											Action:   []string{"rds-db:connect"},
+											Resource: []core.IaCValue{{Resource: &resources.RdsInstance{Name: "instance", CredentialsFile: &core.FileRef{FPath: "rds"}}, Property: resources.RDS_CONNECTION_ARN_IAC_VALUE}},
+										},
+									},
+								},
+							},
+						},
+					},
+					EnvironmentVariables: resources.EnvironmentVariables{"TEST_PERSIST_ORM_CONNECTION": core.IaCValue{
+						Resource: &resources.RdsProxy{
+							Name:  "rds",
+							Role:  &resources.IamRole{Name: "ProxyRole"},
+							Auths: []*resources.ProxyAuth{{SecretArn: core.IaCValue{Resource: &resources.Secret{Name: "Secret"}}}},
+						},
+						Property: string(core.CONNECTION_STRING),
+					},
+					},
+				},
+				&resources.RdsProxy{
+					Name:  "rds",
+					Role:  &resources.IamRole{Name: "ProxyRole"},
+					Auths: []*resources.ProxyAuth{{SecretArn: core.IaCValue{Resource: &resources.Secret{Name: "Secret"}}}},
+				},
+				&resources.SecretVersion{Name: "sv", Path: "rds"},
+			},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+
+			for _, edge := range tt.edge {
+				dag.AddDependencyWithData(edge.Source, edge.Destination, edge.Properties.Data)
+			}
+
+			err := AwsKB.ConfigureFromEdgeData(dag)
+			if !assert.NoError(err) {
+				return
+			}
+			for _, res := range tt.want {
+				assert.Equal(res, dag.GetResource(res.Id()))
+			}
+		})
+	}
+}
diff --git a/pkg/provider/aws/knowledgebase/edge_expansion_test.go b/pkg/provider/aws/knowledgebase/edge_expansion_test.go
new file mode 100644
index 00000000..9a487993
--- /dev/null
+++ b/pkg/provider/aws/knowledgebase/edge_expansion_test.go
@@ -0,0 +1,121 @@
+package knowledgebase
+
+import (
+	"testing"
+
+	dgraph "github.com/dominikbraun/graph"
+	"github.com/klothoplatform/klotho/pkg/annotation"
+	"github.com/klothoplatform/klotho/pkg/core"
+	"github.com/klothoplatform/klotho/pkg/core/coretesting"
+	"github.com/klothoplatform/klotho/pkg/graph"
+	knowledgebase "github.com/klothoplatform/klotho/pkg/knowledge_base"
+	"github.com/klothoplatform/klotho/pkg/provider/aws/resources"
+	"github.com/stretchr/testify/assert"
+)
+
+func Test_ExpandEdges(t *testing.T) {
+	orm := &core.Orm{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}}
+	cases := []struct {
+		name string
+		edge graph.Edge[core.Resource]
+		want coretesting.ResourcesExpectation
+	}{
+		{
+			name: "single rds orm",
+			edge: graph.Edge[core.Resource]{
+				Source:      &resources.LambdaFunction{Name: "lambda"},
+				Destination: &resources.RdsInstance{Name: "rds"},
+				Properties: dgraph.EdgeProperties{
+					Data: knowledgebase.EdgeData{
+						AppName:     "my-app",
+						Source:      &resources.LambdaFunction{Name: "lambda"},
+						Destination: &resources.RdsInstance{Name: "rds"},
+						Constraint: knowledgebase.EdgeConstraint{
+							NodeMustExist:    []core.Resource{&resources.RdsProxy{}},
+							NodeMustNotExist: []core.Resource{&resources.IamRole{}},
+						},
+						EnvironmentVariables: []core.EnvironmentVariable{core.GenerateOrmConnStringEnvVar(orm)},
+					},
+				},
+			},
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:availability_zones:AvailabilityZones",
+					"aws:elastic_ip:my_app_0",
+					"aws:elastic_ip:my_app_1",
+					"aws:iam_policy:my-app-my-app-rds-ormsecretpolicy",
+					"aws:iam_role:my-app-rds-ProxyRole",
+					"aws:internet_gateway:my_app_igw",
+					"aws:lambda_function:lambda",
+					"aws:nat_gateway:my_app_0",
+					"aws:nat_gateway:my_app_1",
+					"aws:rds_instance:rds",
+					"aws:rds_proxy:my-app-rds",
+					"aws:rds_proxy_target_group:my-app-rds",
+					"aws:route_table:my_app_private0",
+					"aws:route_table:my_app_private1",
+					"aws:route_table:my_app_public",
+					"aws:secret:my-app-my-app-rds",
+					"aws:secret_version:my-app-my-app-rds",
+					"aws:security_group:my_app:my-app",
+					"aws:subnet_private:my_app:my_app_private0",
+					"aws:subnet_private:my_app:my_app_private1",
+					"aws:subnet_public:my_app:my_app_public0",
+					"aws:subnet_public:my_app:my_app_public1",
+					"aws:vpc:my_app",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:iam_policy:my-app-my-app-rds-ormsecretpolicy", Destination: "aws:secret_version:my-app-my-app-rds"},
+					{Source: "aws:iam_role:my-app-rds-ProxyRole", Destination: "aws:iam_policy:my-app-my-app-rds-ormsecretpolicy"},
+					{Source: "aws:internet_gateway:my_app_igw", Destination: "aws:vpc:my_app"},
+					{Source: "aws:nat_gateway:my_app_0", Destination: "aws:elastic_ip:my_app_0"},
+					{Source: "aws:nat_gateway:my_app_0", Destination: "aws:subnet_public:my_app:my_app_public0"},
+					{Source: "aws:nat_gateway:my_app_1", Destination: "aws:elastic_ip:my_app_1"},
+					{Source: "aws:nat_gateway:my_app_1", Destination: "aws:subnet_public:my_app:my_app_public1"},
+					{Source: "aws:rds_proxy:my-app-rds", Destination: "aws:iam_role:my-app-rds-ProxyRole"},
+					{Source: "aws:rds_proxy:my-app-rds", Destination: "aws:secret:my-app-my-app-rds"},
+					{Source: "aws:rds_proxy:my-app-rds", Destination: "aws:secret_version:my-app-my-app-rds"},
+					{Source: "aws:rds_proxy:my-app-rds", Destination: "aws:security_group:my_app:my-app"},
+					{Source: "aws:rds_proxy:my-app-rds", Destination: "aws:subnet_private:my_app:my_app_private0"},
+					{Source: "aws:rds_proxy:my-app-rds", Destination: "aws:subnet_private:my_app:my_app_private1"},
+					{Source: "aws:rds_proxy_target_group:my-app-rds", Destination: "aws:rds_instance:rds"},
+					{Source: "aws:rds_proxy_target_group:my-app-rds", Destination: "aws:rds_proxy:my-app-rds"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:nat_gateway:my_app_0"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:subnet_private:my_app:my_app_private0"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:nat_gateway:my_app_1"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:subnet_private:my_app:my_app_private1"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:internet_gateway:my_app_igw"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:subnet_public:my_app:my_app_public0"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:subnet_public:my_app:my_app_public1"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:vpc:my_app"},
+					{Source: "aws:secret_version:my-app-my-app-rds", Destination: "aws:secret:my-app-my-app-rds"},
+					{Source: "aws:security_group:my_app:my-app", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_private:my_app:my_app_private0", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_private:my_app:my_app_private0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_private:my_app:my_app_private1", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_private:my_app:my_app_private1", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_public:my_app:my_app_public0", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_public:my_app:my_app_public0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_public:my_app:my_app_public1", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_public:my_app:my_app_public1", Destination: "aws:vpc:my_app"},
+				},
+			},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+
+			dag.AddDependencyWithData(tt.edge.Source, tt.edge.Destination, tt.edge.Properties.Data)
+
+			err := AwsKB.ExpandEdges(dag)
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+		})
+	}
+}
diff --git a/pkg/provider/aws/orm_test.go b/pkg/provider/aws/orm_test.go
index 37f0b1d8..c07b3a88 100644
--- a/pkg/provider/aws/orm_test.go
+++ b/pkg/provider/aws/orm_test.go
@@ -3,12 +3,98 @@ package aws
 import (
 	"testing"
 
+	"github.com/klothoplatform/klotho/pkg/annotation"
 	"github.com/klothoplatform/klotho/pkg/config"
 	"github.com/klothoplatform/klotho/pkg/core"
 	"github.com/klothoplatform/klotho/pkg/core/coretesting"
 	"github.com/stretchr/testify/assert"
 )
 
+func Test_ExpandOrm(t *testing.T) {
+	orm := &core.Orm{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}}
+	cases := []struct {
+		name   string
+		orm    *core.Orm
+		config *config.Application
+		want   coretesting.ResourcesExpectation
+	}{
+		{
+			name:   "single rds orm",
+			orm:    orm,
+			config: &config.Application{AppName: "my-app", Defaults: config.Defaults{PersistOrm: config.KindDefaults{Type: Rds_postgres}}},
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:availability_zones:AvailabilityZones",
+					"aws:elastic_ip:my_app_0",
+					"aws:elastic_ip:my_app_1",
+					"aws:internet_gateway:my_app_igw",
+					"aws:nat_gateway:my_app_0",
+					"aws:nat_gateway:my_app_1",
+					"aws:rds_instance:my-app-test",
+					"aws:rds_subnet_group:my-app-test",
+					"aws:route_table:my_app_private0",
+					"aws:route_table:my_app_private1",
+					"aws:route_table:my_app_public",
+					"aws:security_group:my_app:my-app",
+					"aws:subnet_private:my_app:my_app_private0",
+					"aws:subnet_private:my_app:my_app_private1",
+					"aws:subnet_public:my_app:my_app_public0",
+					"aws:subnet_public:my_app:my_app_public1",
+					"aws:vpc:my_app",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:internet_gateway:my_app_igw", Destination: "aws:vpc:my_app"},
+					{Source: "aws:nat_gateway:my_app_0", Destination: "aws:elastic_ip:my_app_0"},
+					{Source: "aws:nat_gateway:my_app_0", Destination: "aws:subnet_public:my_app:my_app_public0"},
+					{Source: "aws:nat_gateway:my_app_1", Destination: "aws:elastic_ip:my_app_1"},
+					{Source: "aws:nat_gateway:my_app_1", Destination: "aws:subnet_public:my_app:my_app_public1"},
+					{Source: "aws:rds_instance:my-app-test", Destination: "aws:rds_subnet_group:my-app-test"},
+					{Source: "aws:rds_instance:my-app-test", Destination: "aws:security_group:my_app:my-app"},
+					{Source: "aws:rds_subnet_group:my-app-test", Destination: "aws:subnet_private:my_app:my_app_private0"},
+					{Source: "aws:rds_subnet_group:my-app-test", Destination: "aws:subnet_private:my_app:my_app_private1"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:nat_gateway:my_app_0"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:subnet_private:my_app:my_app_private0"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:nat_gateway:my_app_1"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:subnet_private:my_app:my_app_private1"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:internet_gateway:my_app_igw"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:subnet_public:my_app:my_app_public0"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:subnet_public:my_app:my_app_public1"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:vpc:my_app"},
+					{Source: "aws:security_group:my_app:my-app", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_private:my_app:my_app_private0", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_private:my_app:my_app_private0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_private:my_app:my_app_private1", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_private:my_app:my_app_private1", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_public:my_app:my_app_public0", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_public:my_app:my_app_public0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_public:my_app:my_app_public1", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_public:my_app:my_app_public1", Destination: "aws:vpc:my_app"},
+				},
+			},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+
+			aws := AWS{
+				Config: tt.config,
+			}
+			err := aws.expandOrm(dag, tt.orm)
+
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+			resource := aws.GetResourceTiedToConstruct(tt.orm)
+			assert.NotNil(resource)
+		})
+	}
+}
+
 func Test_GenerateOrmResources(t *testing.T) {
 	appName := "test-app"
 	orm := &core.Orm{AnnotationKey: core.AnnotationKey{ID: "test"}}
diff --git a/pkg/provider/aws/plugin_test.go b/pkg/provider/aws/plugin_test.go
index c0c4c2d7..265ddbca 100644
--- a/pkg/provider/aws/plugin_test.go
+++ b/pkg/provider/aws/plugin_test.go
@@ -4,14 +4,253 @@ import (
 	"fmt"
 	"testing"
 
+	dgraph "github.com/dominikbraun/graph"
 	"github.com/klothoplatform/klotho/pkg/annotation"
 	"github.com/klothoplatform/klotho/pkg/config"
 	"github.com/klothoplatform/klotho/pkg/core"
+	"github.com/klothoplatform/klotho/pkg/core/coretesting"
+	"github.com/klothoplatform/klotho/pkg/graph"
 	"github.com/klothoplatform/klotho/pkg/infra/kubernetes"
+	knowledgebase "github.com/klothoplatform/klotho/pkg/knowledge_base"
 	"github.com/klothoplatform/klotho/pkg/provider/aws/resources"
 	"github.com/stretchr/testify/assert"
 )
 
+func Test_ExpandConstructs(t *testing.T) {
+	eu := &core.ExecutionUnit{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}, DockerfilePath: "path"}
+	orm := &core.Orm{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}}
+	cases := []struct {
+		name       string
+		constructs []core.Construct
+		config     *config.Application
+		want       coretesting.ResourcesExpectation
+	}{
+		{
+			name:       "lambda and rds",
+			constructs: []core.Construct{eu, orm},
+			config: &config.Application{
+				AppName: "my-app",
+				Defaults: config.Defaults{
+					ExecutionUnit: config.KindDefaults{Type: Lambda},
+					PersistOrm:    defaultConfig.PersistOrm,
+				},
+			},
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:availability_zones:AvailabilityZones",
+					"aws:ecr_image:my-app-test",
+					"aws:ecr_repo:my-app",
+					"aws:elastic_ip:my_app_0",
+					"aws:elastic_ip:my_app_1",
+					"aws:iam_role:my-app-test-ExecutionRole",
+					"aws:internet_gateway:my_app_igw",
+					"aws:lambda_function:my-app-test",
+					"aws:log_group:my-app-test",
+					"aws:nat_gateway:my_app_0",
+					"aws:nat_gateway:my_app_1",
+					"aws:rds_instance:my-app-test",
+					"aws:rds_subnet_group:my-app-test",
+					"aws:route_table:my_app_private0",
+					"aws:route_table:my_app_private1",
+					"aws:route_table:my_app_public",
+					"aws:security_group:my_app:my-app",
+					"aws:subnet_private:my_app:my_app_private0",
+					"aws:subnet_private:my_app:my_app_private1",
+					"aws:subnet_public:my_app:my_app_public0",
+					"aws:subnet_public:my_app:my_app_public1",
+					"aws:vpc:my_app",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:ecr_image:my-app-test", Destination: "aws:ecr_repo:my-app"},
+					{Source: "aws:internet_gateway:my_app_igw", Destination: "aws:vpc:my_app"},
+					{Source: "aws:lambda_function:my-app-test", Destination: "aws:ecr_image:my-app-test"},
+					{Source: "aws:lambda_function:my-app-test", Destination: "aws:iam_role:my-app-test-ExecutionRole"},
+					{Source: "aws:lambda_function:my-app-test", Destination: "aws:log_group:my-app-test"},
+					{Source: "aws:nat_gateway:my_app_0", Destination: "aws:elastic_ip:my_app_0"},
+					{Source: "aws:nat_gateway:my_app_0", Destination: "aws:subnet_public:my_app:my_app_public0"},
+					{Source: "aws:nat_gateway:my_app_1", Destination: "aws:elastic_ip:my_app_1"},
+					{Source: "aws:nat_gateway:my_app_1", Destination: "aws:subnet_public:my_app:my_app_public1"},
+					{Source: "aws:rds_instance:my-app-test", Destination: "aws:rds_subnet_group:my-app-test"},
+					{Source: "aws:rds_instance:my-app-test", Destination: "aws:security_group:my_app:my-app"},
+					{Source: "aws:rds_subnet_group:my-app-test", Destination: "aws:subnet_private:my_app:my_app_private0"},
+					{Source: "aws:rds_subnet_group:my-app-test", Destination: "aws:subnet_private:my_app:my_app_private1"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:nat_gateway:my_app_0"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:subnet_private:my_app:my_app_private0"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:nat_gateway:my_app_1"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:subnet_private:my_app:my_app_private1"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:internet_gateway:my_app_igw"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:subnet_public:my_app:my_app_public0"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:subnet_public:my_app:my_app_public1"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:vpc:my_app"},
+					{Source: "aws:security_group:my_app:my-app", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_private:my_app:my_app_private0", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_private:my_app:my_app_private0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_private:my_app:my_app_private1", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_private:my_app:my_app_private1", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_public:my_app:my_app_public0", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_public:my_app:my_app_public0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_public:my_app:my_app_public1", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_public:my_app:my_app_public1", Destination: "aws:vpc:my_app"},
+				},
+			},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+			result := core.NewConstructGraph()
+
+			for _, construct := range tt.constructs {
+				result.AddConstruct(construct)
+			}
+
+			aws := AWS{
+				Config: tt.config,
+			}
+			err := aws.ExpandConstructs(result, dag)
+
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+		})
+	}
+}
+
+func Test_CopyConstructEdgesToDag(t *testing.T) {
+	orm := &core.Orm{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}}
+
+	eu := &core.ExecutionUnit{
+		AnnotationKey:        core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability},
+		EnvironmentVariables: core.EnvironmentVariables{core.GenerateOrmConnStringEnvVar(orm)},
+	}
+	cases := []struct {
+		name                 string
+		constructs           []graph.Edge[core.Construct]
+		config               *config.Application
+		constructResourceMap map[string]core.Resource
+		want                 []*graph.Edge[core.Resource]
+	}{
+		{
+			name: "lambda and rds",
+			constructs: []graph.Edge[core.Construct]{
+				{Source: eu, Destination: orm},
+			},
+			config: &config.Application{
+				AppName: "my-app",
+			},
+			constructResourceMap: map[string]core.Resource{
+				"execution_unit:test": &resources.LambdaFunction{Name: "lambda"},
+				"persist:test":        &resources.RdsInstance{Name: "rds"},
+			},
+			want: []*graph.Edge[core.Resource]{
+				{Source: &resources.LambdaFunction{Name: "lambda"}, Destination: &resources.RdsInstance{Name: "rds"}, Properties: dgraph.EdgeProperties{
+					Attributes: make(map[string]string),
+					Data: knowledgebase.EdgeData{
+						AppName:     "my-app",
+						Source:      &resources.LambdaFunction{Name: "lambda"},
+						Destination: &resources.RdsInstance{Name: "rds"},
+						Constraint: knowledgebase.EdgeConstraint{
+							NodeMustExist:    []core.Resource{&resources.RdsProxy{}},
+							NodeMustNotExist: []core.Resource{&resources.IamRole{}},
+						},
+						EnvironmentVariables: []core.EnvironmentVariable{core.GenerateOrmConnStringEnvVar(orm)},
+					},
+				}},
+			},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+			result := core.NewConstructGraph()
+
+			for _, dep := range tt.constructs {
+				result.AddConstruct(dep.Source)
+				result.AddConstruct(dep.Destination)
+				result.AddDependency(dep.Source.Id(), dep.Destination.Id())
+			}
+			for _, res := range tt.constructResourceMap {
+				dag.AddResource(res)
+			}
+			aws := AWS{
+				Config:                tt.config,
+				constructIdToResource: tt.constructResourceMap,
+			}
+			err := aws.CopyConstructEdgesToDag(result, dag)
+
+			if !assert.NoError(err) {
+				return
+			}
+			for _, dep := range tt.want {
+				edge := dag.GetDependency(dep.Source, dep.Destination)
+				assert.Equal(edge, dep)
+			}
+		})
+	}
+}
+
+func Test_configureResources(t *testing.T) {
+
+	cases := []struct {
+		name       string
+		config     *config.Application
+		constructs []core.Construct
+		resources  []core.Resource
+		want       []core.Resource
+	}{
+		{
+			name: "lambda and rds",
+			config: &config.Application{
+				AppName: "my-app",
+				ExecutionUnits: map[string]*config.ExecutionUnit{
+					"test": &config.ExecutionUnit{
+						InfraParams: config.ConvertToInfraParams(config.ServerlessTypeParams{Timeout: 100, Memory: 200}),
+					},
+				},
+			},
+			constructs: []core.Construct{
+				&core.ExecutionUnit{
+					AnnotationKey:        core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability},
+					EnvironmentVariables: core.EnvironmentVariables{core.NewEnvironmentVariable("env1", nil, "val1")}},
+			},
+			resources: []core.Resource{&resources.LambdaFunction{Name: "lambda", ConstructsRef: []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}}}, &resources.RdsProxy{Name: "rds"}},
+			want: []core.Resource{
+				&resources.LambdaFunction{Name: "lambda", Timeout: 100, MemorySize: 200, ConstructsRef: []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}}, EnvironmentVariables: resources.EnvironmentVariables{"env1": core.IaCValue{Property: "val1"}}},
+				&resources.RdsProxy{Name: "rds", EngineFamily: "POSTGRESQL", IdleClientTimeout: 1800}},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+			result := core.NewConstructGraph()
+			for _, construct := range tt.constructs {
+				result.AddConstruct(construct)
+			}
+			for _, res := range tt.resources {
+				dag.AddResource(res)
+			}
+			aws := AWS{
+				Config: tt.config,
+			}
+			err := aws.configureResources(result, dag)
+
+			if !assert.NoError(err) {
+				return
+			}
+			for _, res := range tt.want {
+				graphRes := dag.GetResource(res.Id())
+				assert.Equal(graphRes, res)
+			}
+		})
+	}
+}
+
 func Test_shouldCreateNetwork(t *testing.T) {
 	cases := []struct {
 		name       string
diff --git a/pkg/provider/aws/provider_test.go b/pkg/provider/aws/provider_test.go
index 98f6bb5a..9168a85f 100644
--- a/pkg/provider/aws/provider_test.go
+++ b/pkg/provider/aws/provider_test.go
@@ -50,6 +50,26 @@ func TestAwsMapResourceDirectlyToConstruct(t *testing.T) {
 	})
 }
 
+func TestAwsMapResourceToConstruct(t *testing.T) {
+	t.Run("empty AWS struct, MapResourceDirectlyToConstruct", func(t *testing.T) {
+		assert := assert.New(t)
+		a := AWS{}
+		err := a.MapResourceToConstruct(dummyResource("res"), dummyConstruct("cons"))
+		assert.NoError(err)
+		assert.Equal(
+			map[string]core.Resource{
+				"cons": dummyResource("res"),
+			},
+			a.constructIdToResource)
+	})
+	t.Run("empty AWS struct, MapResourceDirectlyToConstruct", func(t *testing.T) {
+		assert := assert.New(t)
+		a := AWS{}
+		res := a.GetResourceTiedToConstruct(dummyConstruct("cons"))
+		assert.Nil(res)
+	})
+}
+
 type (
 	dummyResource  string
 	dummyConstruct string
@@ -60,6 +80,9 @@ func (dr dummyResource) KlothoConstructRef() []core.AnnotationKey { return nil }
 func (dr dummyResource) Id() core.ResourceId {
 	return core.ResourceId{Provider: "test", Type: "", Name: string(dr)}
 }
+func (f dummyResource) Create(dag *core.ResourceGraph, metadata map[string]any) (core.Resource, error) {
+	return nil, nil
+}
 
 func (dc dummyConstruct) Provenance() core.AnnotationKey { return core.AnnotationKey{} }
 
diff --git a/pkg/provider/aws/resources/cloudwatch_test.go b/pkg/provider/aws/resources/cloudwatch_test.go
new file mode 100644
index 00000000..844805b3
--- /dev/null
+++ b/pkg/provider/aws/resources/cloudwatch_test.go
@@ -0,0 +1,73 @@
+package resources
+
+import (
+	"testing"
+
+	"github.com/klothoplatform/klotho/pkg/annotation"
+	"github.com/klothoplatform/klotho/pkg/core"
+	"github.com/klothoplatform/klotho/pkg/core/coretesting"
+	"github.com/stretchr/testify/assert"
+)
+
+func Test_CloudwatchLogGroupCreate(t *testing.T) {
+	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	cases := []struct {
+		name     string
+		logGroup *LogGroup
+		want     coretesting.ResourcesExpectation
+	}{
+		{
+			name: "nil repo",
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:log_group:my-app-log-group",
+				},
+				Deps: []coretesting.StringDep{},
+			},
+		},
+		{
+			name:     "existing repo",
+			logGroup: &LogGroup{Name: "my-app-log-group", ConstructsRef: initialRefs},
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:log_group:my-app-log-group",
+				},
+				Deps: []coretesting.StringDep{},
+			},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+			if tt.logGroup != nil {
+				dag.AddResource(tt.logGroup)
+			}
+			metadata := CloudwatchLogGroupCreateParams{
+				AppName: "my-app",
+				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Name:    "log-group",
+			}
+
+			logGroup := &LogGroup{}
+			err := logGroup.Create(dag, metadata)
+
+			if !assert.NoError(err) {
+				return
+			}
+
+			tt.want.Assert(t, dag)
+
+			graphLogGroup := dag.GetResourceByVertexId(logGroup.Id().String())
+			logGroup = graphLogGroup.(*LogGroup)
+
+			assert.Equal(logGroup.Name, "my-app-log-group")
+			if tt.logGroup == nil {
+				assert.ElementsMatch(logGroup.ConstructsRef, metadata.Refs)
+			} else {
+				assert.Equal(logGroup, tt.logGroup)
+				assert.ElementsMatch(logGroup.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+			}
+		})
+	}
+}
diff --git a/pkg/provider/aws/resources/ecr_test.go b/pkg/provider/aws/resources/ecr_test.go
index 9d989921..e8eb3ba7 100644
--- a/pkg/provider/aws/resources/ecr_test.go
+++ b/pkg/provider/aws/resources/ecr_test.go
@@ -5,9 +5,128 @@ import (
 
 	"github.com/klothoplatform/klotho/pkg/annotation"
 	"github.com/klothoplatform/klotho/pkg/core"
+	"github.com/klothoplatform/klotho/pkg/core/coretesting"
 	"github.com/stretchr/testify/assert"
 )
 
+func Test_RepositoryCreate(t *testing.T) {
+	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	cases := []struct {
+		name string
+		repo *EcrRepository
+		want coretesting.ResourcesExpectation
+	}{
+		{
+			name: "nil repo",
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:ecr_repo:my-app",
+				},
+				Deps: []coretesting.StringDep{},
+			},
+		},
+		{
+			name: "existing repo",
+			repo: &EcrRepository{Name: "my-app", ConstructsRef: initialRefs, ForceDelete: true},
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:ecr_repo:my-app",
+				},
+				Deps: []coretesting.StringDep{},
+			},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+			if tt.repo != nil {
+				dag.AddResource(tt.repo)
+			}
+			metadata := RepoCreateParams{
+				AppName: "my-app",
+				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+			}
+
+			repo := &EcrRepository{}
+			err := repo.Create(dag, metadata)
+
+			if !assert.NoError(err) {
+				return
+			}
+
+			tt.want.Assert(t, dag)
+			graphRepo := dag.GetResource(repo.Id())
+			repo = graphRepo.(*EcrRepository)
+			assert.Equal(repo.Name, "my-app")
+			if tt.repo == nil {
+				assert.ElementsMatch(repo.ConstructsRef, metadata.Refs)
+			} else {
+				assert.Equal(repo, tt.repo)
+				assert.ElementsMatch(repo.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+			}
+		})
+	}
+}
+
+func Test_ImageCreate(t *testing.T) {
+	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	cases := []struct {
+		name    string
+		image   *EcrImage
+		want    coretesting.ResourcesExpectation
+		wantErr bool
+	}{
+		{
+			name: "nil repo",
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:ecr_image:my-app-test-unit",
+					"aws:ecr_repo:my-app",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:ecr_image:my-app-test-unit", Destination: "aws:ecr_repo:my-app"},
+				},
+			},
+		},
+		{
+			name:    "existing image",
+			image:   &EcrImage{Name: "my-app-test-unit", ConstructsRef: initialRefs},
+			wantErr: true,
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+			if tt.image != nil {
+				dag.AddResource(tt.image)
+			}
+			metadata := ImageCreateParams{
+				AppName: "my-app",
+				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Name:    "test-unit",
+			}
+			image := &EcrImage{}
+			err := image.Create(dag, metadata)
+
+			if tt.wantErr {
+				assert.Error(err)
+				return
+			}
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+			graphImage := dag.GetResource(image.Id())
+			image = graphImage.(*EcrImage)
+
+			assert.Equal(image.Name, "my-app-test-unit")
+			assert.Equal(image.ConstructsRef, metadata.Refs)
+		})
+	}
+}
+
 func Test_GenerateExecUnitResources(t *testing.T) {
 	appName := "test-app"
 	unit := &core.ExecutionUnit{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}}
diff --git a/pkg/provider/aws/resources/iam_test.go b/pkg/provider/aws/resources/iam_test.go
index a26e206e..6f132040 100644
--- a/pkg/provider/aws/resources/iam_test.go
+++ b/pkg/provider/aws/resources/iam_test.go
@@ -3,10 +3,118 @@ package resources
 import (
 	"testing"
 
+	"github.com/klothoplatform/klotho/pkg/annotation"
 	"github.com/klothoplatform/klotho/pkg/core"
+	"github.com/klothoplatform/klotho/pkg/core/coretesting"
 	"github.com/stretchr/testify/assert"
 )
 
+func Test_RoleCreate(t *testing.T) {
+	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	cases := []struct {
+		name    string
+		role    *IamRole
+		want    coretesting.ResourcesExpectation
+		wantErr bool
+	}{
+		{
+			name: "nil role",
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:iam_role:my-app-executionRole",
+				},
+				Deps: []coretesting.StringDep{},
+			},
+		},
+		{
+			name:    "existing role",
+			role:    &IamRole{Name: "my-app-executionRole", ConstructsRef: initialRefs},
+			wantErr: true,
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+			if tt.role != nil {
+				dag.AddResource(tt.role)
+			}
+			metadata := RoleCreateParams{
+				AppName: "my-app",
+				Name:    "executionRole",
+				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+			}
+			role := &IamRole{}
+			err := role.Create(dag, metadata)
+
+			if tt.wantErr {
+				assert.Error(err)
+				return
+			}
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+
+			assert.Equal(role.Name, "my-app-executionRole")
+			assert.Equal(role.ConstructsRef, metadata.Refs)
+		})
+	}
+}
+
+func Test_PolicyCreate(t *testing.T) {
+	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	cases := []struct {
+		name    string
+		policy  *IamPolicy
+		want    coretesting.ResourcesExpectation
+		wantErr bool
+	}{
+		{
+			name: "nil policy",
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:iam_policy:my-app-policy",
+				},
+				Deps: []coretesting.StringDep{},
+			},
+		},
+		{
+			name:    "existing policy",
+			policy:  &IamPolicy{Name: "my-app-policy", ConstructsRef: initialRefs},
+			wantErr: true,
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+			if tt.policy != nil {
+				dag.AddResource(tt.policy)
+			}
+			metadata := IamPolicyCreateParams{
+				AppName: "my-app",
+				Name:    "policy",
+				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+			}
+			policy := &IamPolicy{}
+			err := policy.Create(dag, metadata)
+
+			if tt.wantErr {
+				assert.Error(err)
+				return
+			}
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+
+			assert.Equal(policy.Name, "my-app-policy")
+			assert.Equal(policy.ConstructsRef, metadata.Refs)
+		})
+	}
+}
+
 func Test_AddAllowPolicyToUnit(t *testing.T) {
 	bucket := NewS3Bucket(&core.Fs{}, "test-app")
 	unitId := "testUnit"
diff --git a/pkg/provider/aws/resources/lambda_test.go b/pkg/provider/aws/resources/lambda_test.go
new file mode 100644
index 00000000..2a669bc2
--- /dev/null
+++ b/pkg/provider/aws/resources/lambda_test.go
@@ -0,0 +1,77 @@
+package resources
+
+import (
+	"testing"
+
+	"github.com/klothoplatform/klotho/pkg/annotation"
+	"github.com/klothoplatform/klotho/pkg/core"
+	"github.com/klothoplatform/klotho/pkg/core/coretesting"
+	"github.com/stretchr/testify/assert"
+)
+
+func Test_LambdaCreate(t *testing.T) {
+	eu := &core.ExecutionUnit{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}}
+	cases := []struct {
+		name    string
+		lambda  *LambdaFunction
+		want    coretesting.ResourcesExpectation
+		wantErr bool
+	}{
+		{
+			name: "nil lambda",
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:ecr_image:my-app-test",
+					"aws:ecr_repo:my-app",
+					"aws:iam_role:my-app-test-ExecutionRole",
+					"aws:lambda_function:my-app-test",
+					"aws:log_group:my-app-test",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:ecr_image:my-app-test", Destination: "aws:ecr_repo:my-app"},
+					{Source: "aws:lambda_function:my-app-test", Destination: "aws:ecr_image:my-app-test"},
+					{Source: "aws:lambda_function:my-app-test", Destination: "aws:iam_role:my-app-test-ExecutionRole"},
+					{Source: "aws:lambda_function:my-app-test", Destination: "aws:log_group:my-app-test"},
+				},
+			},
+		},
+		{
+			name:    "existing lambda",
+			lambda:  &LambdaFunction{Name: "my-app-test"},
+			wantErr: true,
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+
+			if tt.lambda != nil {
+				dag.AddResource(tt.lambda)
+			}
+
+			metadata := LambdaCreateParams{
+				AppName: "my-app",
+				Refs:    []core.AnnotationKey{eu.AnnotationKey},
+				Name:    eu.ID,
+			}
+			lambda := &LambdaFunction{}
+			err := lambda.Create(dag, metadata)
+			if tt.wantErr {
+				assert.Error(err)
+				return
+			}
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+
+			graphLambda := dag.GetResource(lambda.Id())
+			lambda = graphLambda.(*LambdaFunction)
+
+			assert.Equal(lambda.Name, "my-app-test")
+			assert.ElementsMatch(lambda.ConstructsRef, []core.AnnotationKey{eu.AnnotationKey})
+		})
+	}
+}
diff --git a/pkg/provider/aws/resources/rds_test.go b/pkg/provider/aws/resources/rds_test.go
index 83c83ed9..a4d7a47f 100644
--- a/pkg/provider/aws/resources/rds_test.go
+++ b/pkg/provider/aws/resources/rds_test.go
@@ -4,12 +4,397 @@ import (
 	"fmt"
 	"testing"
 
+	"github.com/klothoplatform/klotho/pkg/annotation"
 	"github.com/klothoplatform/klotho/pkg/config"
 	"github.com/klothoplatform/klotho/pkg/core"
 	"github.com/klothoplatform/klotho/pkg/core/coretesting"
 	"github.com/stretchr/testify/assert"
 )
 
+func Test_RdsInstanceCreate(t *testing.T) {
+	orm := &core.Orm{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}}
+	cases := []struct {
+		name     string
+		instance *RdsInstance
+		want     coretesting.ResourcesExpectation
+		wantErr  bool
+	}{
+		{
+			name: "nil instance",
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:availability_zones:AvailabilityZones",
+					"aws:elastic_ip:my_app_0",
+					"aws:elastic_ip:my_app_1",
+					"aws:internet_gateway:my_app_igw",
+					"aws:nat_gateway:my_app_0",
+					"aws:nat_gateway:my_app_1",
+					"aws:rds_instance:my-app-test",
+					"aws:rds_subnet_group:my-app-test",
+					"aws:route_table:my_app_private0",
+					"aws:route_table:my_app_private1",
+					"aws:route_table:my_app_public",
+					"aws:security_group:my_app:my-app",
+					"aws:subnet_private:my_app:my_app_private0",
+					"aws:subnet_private:my_app:my_app_private1",
+					"aws:subnet_public:my_app:my_app_public0",
+					"aws:subnet_public:my_app:my_app_public1",
+					"aws:vpc:my_app",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:internet_gateway:my_app_igw", Destination: "aws:vpc:my_app"},
+					{Source: "aws:nat_gateway:my_app_0", Destination: "aws:elastic_ip:my_app_0"},
+					{Source: "aws:nat_gateway:my_app_0", Destination: "aws:subnet_public:my_app:my_app_public0"},
+					{Source: "aws:nat_gateway:my_app_1", Destination: "aws:elastic_ip:my_app_1"},
+					{Source: "aws:nat_gateway:my_app_1", Destination: "aws:subnet_public:my_app:my_app_public1"},
+					{Source: "aws:rds_instance:my-app-test", Destination: "aws:rds_subnet_group:my-app-test"},
+					{Source: "aws:rds_instance:my-app-test", Destination: "aws:security_group:my_app:my-app"},
+					{Source: "aws:rds_subnet_group:my-app-test", Destination: "aws:subnet_private:my_app:my_app_private0"},
+					{Source: "aws:rds_subnet_group:my-app-test", Destination: "aws:subnet_private:my_app:my_app_private1"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:nat_gateway:my_app_0"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:subnet_private:my_app:my_app_private0"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:nat_gateway:my_app_1"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:subnet_private:my_app:my_app_private1"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:internet_gateway:my_app_igw"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:subnet_public:my_app:my_app_public0"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:subnet_public:my_app:my_app_public1"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:vpc:my_app"},
+					{Source: "aws:security_group:my_app:my-app", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_private:my_app:my_app_private0", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_private:my_app:my_app_private0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_private:my_app:my_app_private1", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_private:my_app:my_app_private1", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_public:my_app:my_app_public0", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_public:my_app:my_app_public0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_public:my_app:my_app_public1", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_public:my_app:my_app_public1", Destination: "aws:vpc:my_app"},
+				},
+			},
+		},
+		{
+			name:     "existing instance",
+			instance: &RdsInstance{Name: "my-app-test"},
+			wantErr:  true,
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+
+			if tt.instance != nil {
+				dag.AddResource(tt.instance)
+			}
+
+			metadata := RdsInstanceCreateParams{
+				AppName: "my-app",
+				Refs:    []core.AnnotationKey{orm.AnnotationKey},
+				Name:    orm.ID,
+			}
+			instance := &RdsInstance{}
+			err := instance.Create(dag, metadata)
+			if tt.wantErr {
+				assert.Error(err)
+				return
+			}
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+
+			graphInstance := dag.GetResource(instance.Id())
+			instance = graphInstance.(*RdsInstance)
+
+			assert.Equal(instance.Name, "my-app-test")
+			assert.ElementsMatch(instance.ConstructsRef, []core.AnnotationKey{orm.AnnotationKey})
+		})
+	}
+}
+
+func Test_RdsSubnetGroupCreate(t *testing.T) {
+	orm := &core.Orm{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}}
+	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	cases := []struct {
+		name        string
+		subnetGroup *RdsSubnetGroup
+		want        coretesting.ResourcesExpectation
+	}{
+		{
+			name: "nil instance",
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:availability_zones:AvailabilityZones",
+					"aws:elastic_ip:my_app_0",
+					"aws:elastic_ip:my_app_1",
+					"aws:internet_gateway:my_app_igw",
+					"aws:nat_gateway:my_app_0",
+					"aws:nat_gateway:my_app_1",
+					"aws:rds_subnet_group:my-app-test",
+					"aws:route_table:my_app_private0",
+					"aws:route_table:my_app_private1",
+					"aws:route_table:my_app_public",
+					"aws:subnet_private:my_app:my_app_private0",
+					"aws:subnet_private:my_app:my_app_private1",
+					"aws:subnet_public:my_app:my_app_public0",
+					"aws:subnet_public:my_app:my_app_public1",
+					"aws:vpc:my_app",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:internet_gateway:my_app_igw", Destination: "aws:vpc:my_app"},
+					{Source: "aws:nat_gateway:my_app_0", Destination: "aws:elastic_ip:my_app_0"},
+					{Source: "aws:nat_gateway:my_app_0", Destination: "aws:subnet_public:my_app:my_app_public0"},
+					{Source: "aws:nat_gateway:my_app_1", Destination: "aws:elastic_ip:my_app_1"},
+					{Source: "aws:nat_gateway:my_app_1", Destination: "aws:subnet_public:my_app:my_app_public1"},
+					{Source: "aws:rds_subnet_group:my-app-test", Destination: "aws:subnet_private:my_app:my_app_private0"},
+					{Source: "aws:rds_subnet_group:my-app-test", Destination: "aws:subnet_private:my_app:my_app_private1"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:nat_gateway:my_app_0"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:subnet_private:my_app:my_app_private0"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:nat_gateway:my_app_1"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:subnet_private:my_app:my_app_private1"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:internet_gateway:my_app_igw"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:subnet_public:my_app:my_app_public0"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:subnet_public:my_app:my_app_public1"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_private:my_app:my_app_private0", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_private:my_app:my_app_private0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_private:my_app:my_app_private1", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_private:my_app:my_app_private1", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_public:my_app:my_app_public0", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_public:my_app:my_app_public0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_public:my_app:my_app_public1", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_public:my_app:my_app_public1", Destination: "aws:vpc:my_app"},
+				},
+			},
+		},
+		{
+			name:        "existing instance",
+			subnetGroup: &RdsSubnetGroup{Name: "my-app-test", ConstructsRef: initialRefs},
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:rds_subnet_group:my-app-test",
+				},
+				Deps: []coretesting.StringDep{},
+			},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+
+			if tt.subnetGroup != nil {
+				dag.AddResource(tt.subnetGroup)
+			}
+
+			metadata := RdsSubnetGroupCreateParams{
+				AppName: "my-app",
+				Refs:    []core.AnnotationKey{orm.AnnotationKey},
+				Name:    orm.ID,
+			}
+			subnetGroup := &RdsSubnetGroup{}
+			err := subnetGroup.Create(dag, metadata)
+
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+
+			graphSG := dag.GetResource(subnetGroup.Id())
+			subnetGroup = graphSG.(*RdsSubnetGroup)
+
+			assert.Equal(subnetGroup.Name, "my-app-test")
+			if tt.subnetGroup == nil {
+				assert.Len(subnetGroup.Subnets, 2)
+				assert.ElementsMatch(subnetGroup.ConstructsRef, metadata.Refs)
+			} else {
+				assert.ElementsMatch(subnetGroup.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}))
+			}
+		})
+	}
+}
+
+func Test_RdsProxyCreate(t *testing.T) {
+	orm := &core.Orm{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}}
+	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	cases := []struct {
+		name  string
+		proxy *RdsProxy
+		want  coretesting.ResourcesExpectation
+	}{
+		{
+			name: "nil proxy",
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:availability_zones:AvailabilityZones",
+					"aws:elastic_ip:my_app_0",
+					"aws:elastic_ip:my_app_1",
+					"aws:internet_gateway:my_app_igw",
+					"aws:nat_gateway:my_app_0",
+					"aws:nat_gateway:my_app_1",
+					"aws:rds_proxy:my-app-test",
+					"aws:route_table:my_app_private0",
+					"aws:route_table:my_app_private1",
+					"aws:route_table:my_app_public",
+					"aws:security_group:my_app:my-app",
+					"aws:subnet_private:my_app:my_app_private0",
+					"aws:subnet_private:my_app:my_app_private1",
+					"aws:subnet_public:my_app:my_app_public0",
+					"aws:subnet_public:my_app:my_app_public1",
+					"aws:vpc:my_app",
+					"aws:iam_role:my-app-test-ProxyRole",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:internet_gateway:my_app_igw", Destination: "aws:vpc:my_app"},
+					{Source: "aws:nat_gateway:my_app_0", Destination: "aws:elastic_ip:my_app_0"},
+					{Source: "aws:nat_gateway:my_app_0", Destination: "aws:subnet_public:my_app:my_app_public0"},
+					{Source: "aws:nat_gateway:my_app_1", Destination: "aws:elastic_ip:my_app_1"},
+					{Source: "aws:nat_gateway:my_app_1", Destination: "aws:subnet_public:my_app:my_app_public1"},
+					{Source: "aws:rds_proxy:my-app-test", Destination: "aws:security_group:my_app:my-app"},
+					{Source: "aws:rds_proxy:my-app-test", Destination: "aws:subnet_private:my_app:my_app_private0"},
+					{Source: "aws:rds_proxy:my-app-test", Destination: "aws:subnet_private:my_app:my_app_private1"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:nat_gateway:my_app_0"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:subnet_private:my_app:my_app_private0"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:nat_gateway:my_app_1"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:subnet_private:my_app:my_app_private1"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:internet_gateway:my_app_igw"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:subnet_public:my_app:my_app_public0"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:subnet_public:my_app:my_app_public1"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:vpc:my_app"},
+					{Source: "aws:security_group:my_app:my-app", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_private:my_app:my_app_private0", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_private:my_app:my_app_private0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_private:my_app:my_app_private1", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_private:my_app:my_app_private1", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_public:my_app:my_app_public0", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_public:my_app:my_app_public0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_public:my_app:my_app_public1", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_public:my_app:my_app_public1", Destination: "aws:vpc:my_app"},
+					{Source: "aws:rds_proxy:my-app-test", Destination: "aws:iam_role:my-app-test-ProxyRole"},
+				},
+			},
+		},
+		{
+			name:  "existing instance",
+			proxy: &RdsProxy{Name: "my-app-test", ConstructsRef: initialRefs},
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:rds_proxy:my-app-test",
+				},
+				Deps: []coretesting.StringDep{},
+			},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+
+			if tt.proxy != nil {
+				dag.AddResource(tt.proxy)
+			}
+
+			metadata := RdsProxyCreateParams{
+				AppName: "my-app",
+				Refs:    []core.AnnotationKey{orm.AnnotationKey},
+				Name:    orm.ID,
+			}
+			proxy := &RdsProxy{}
+			err := proxy.Create(dag, metadata)
+
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+
+			graphSG := dag.GetResource(proxy.Id())
+			proxy = graphSG.(*RdsProxy)
+
+			assert.Equal(proxy.Name, "my-app-test")
+			if tt.proxy == nil {
+				assert.Len(proxy.Subnets, 2)
+				assert.Len(proxy.SecurityGroups, 1)
+				assert.NotNil(proxy.Role)
+				assert.Equal(proxy.ConstructsRef, metadata.Refs)
+			} else {
+				assert.Equal(proxy.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}))
+			}
+		})
+	}
+}
+
+func Test_RdsProxyTargetGroupCreate(t *testing.T) {
+	orm := &core.Orm{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}}
+	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	cases := []struct {
+		name        string
+		targetGroup *RdsProxyTargetGroup
+		want        coretesting.ResourcesExpectation
+	}{
+		{
+			name: "nil proxy",
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:rds_proxy_target_group:my-app-test",
+				},
+				Deps: []coretesting.StringDep{},
+			},
+		},
+		{
+			name:        "existing instance",
+			targetGroup: &RdsProxyTargetGroup{Name: "my-app-test", ConstructsRef: initialRefs},
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:rds_proxy_target_group:my-app-test",
+				},
+				Deps: []coretesting.StringDep{},
+			},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+
+			if tt.targetGroup != nil {
+				dag.AddResource(tt.targetGroup)
+			}
+
+			metadata := RdsProxyTargetGroupCreateParams{
+				AppName: "my-app",
+				Refs:    []core.AnnotationKey{orm.AnnotationKey},
+				Name:    orm.ID,
+			}
+			targetGroup := &RdsProxyTargetGroup{}
+			err := targetGroup.Create(dag, metadata)
+
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+
+			graphSG := dag.GetResource(targetGroup.Id())
+			targetGroup = graphSG.(*RdsProxyTargetGroup)
+
+			assert.Equal(targetGroup.Name, "my-app-test")
+			if tt.targetGroup == nil {
+				assert.Equal(targetGroup.ConstructsRef, metadata.Refs)
+			} else {
+				assert.Equal(targetGroup.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}))
+			}
+		})
+	}
+}
+
 func Test_CreateRdsInstance(t *testing.T) {
 	appName := "test-app"
 	orm := &core.Orm{AnnotationKey: core.AnnotationKey{ID: "test", Capability: "orm"}}
diff --git a/pkg/provider/aws/resources/secret_test.go b/pkg/provider/aws/resources/secret_test.go
new file mode 100644
index 00000000..146f2f08
--- /dev/null
+++ b/pkg/provider/aws/resources/secret_test.go
@@ -0,0 +1,127 @@
+package resources
+
+import (
+	"testing"
+
+	"github.com/klothoplatform/klotho/pkg/annotation"
+	"github.com/klothoplatform/klotho/pkg/core"
+	"github.com/klothoplatform/klotho/pkg/core/coretesting"
+	"github.com/stretchr/testify/assert"
+)
+
+func Test_SecretCreate(t *testing.T) {
+	cases := []struct {
+		name    string
+		secret  *Secret
+		want    coretesting.ResourcesExpectation
+		wantErr bool
+	}{
+		{
+			name: "nil secret",
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:secret:my-app-test",
+				},
+				Deps: []coretesting.StringDep{},
+			},
+		},
+		{
+			name:    "existing secret",
+			secret:  &Secret{Name: "my-app-test"},
+			wantErr: true,
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+
+			if tt.secret != nil {
+				dag.AddResource(tt.secret)
+			}
+
+			metadata := SecretCreateParams{
+				AppName: "my-app",
+				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.PersistCapability}},
+				Name:    "test",
+			}
+			secret := &Secret{}
+			err := secret.Create(dag, metadata)
+			if tt.wantErr {
+				assert.Error(err)
+				return
+			}
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+
+			graphInstance := dag.GetResource(secret.Id())
+			secret = graphInstance.(*Secret)
+
+			assert.Equal(secret.Name, "my-app-test")
+			assert.ElementsMatch(secret.ConstructsRef, metadata.Refs)
+		})
+	}
+}
+
+func Test_SecretVersionCreate(t *testing.T) {
+	cases := []struct {
+		name    string
+		secret  *SecretVersion
+		want    coretesting.ResourcesExpectation
+		wantErr bool
+	}{
+		{
+			name: "nil secret",
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:secret:my-app-test",
+					"aws:secret_version:my-app-test",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:secret_version:my-app-test", Destination: "aws:secret:my-app-test"},
+				},
+			},
+		},
+		{
+			name:    "existing secret",
+			secret:  &SecretVersion{Name: "my-app-test"},
+			wantErr: true,
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+
+			if tt.secret != nil {
+				dag.AddResource(tt.secret)
+			}
+
+			metadata := SecretVersionCreateParams{
+				AppName: "my-app",
+				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.PersistCapability}},
+				Name:    "test",
+			}
+			secret := &SecretVersion{}
+			err := secret.Create(dag, metadata)
+			if tt.wantErr {
+				assert.Error(err)
+				return
+			}
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+
+			graphInstance := dag.GetResource(secret.Id())
+			secret = graphInstance.(*SecretVersion)
+
+			assert.Equal(secret.Name, "my-app-test")
+			assert.ElementsMatch(secret.ConstructsRef, metadata.Refs)
+		})
+	}
+}
diff --git a/pkg/provider/aws/resources/securitygroup_test.go b/pkg/provider/aws/resources/securitygroup_test.go
index 4cc86fb4..b2c64932 100644
--- a/pkg/provider/aws/resources/securitygroup_test.go
+++ b/pkg/provider/aws/resources/securitygroup_test.go
@@ -3,11 +3,78 @@ package resources
 import (
 	"testing"
 
+	"github.com/klothoplatform/klotho/pkg/annotation"
 	"github.com/klothoplatform/klotho/pkg/config"
 	"github.com/klothoplatform/klotho/pkg/core"
+	"github.com/klothoplatform/klotho/pkg/core/coretesting"
 	"github.com/stretchr/testify/assert"
 )
 
+func Test_SecurityGroupCreate(t *testing.T) {
+	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	cases := []struct {
+		name string
+		sg   *SecurityGroup
+		want coretesting.ResourcesExpectation
+	}{
+		{
+			name: "nil sg",
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:security_group:my_app:my-app",
+					"aws:vpc:my_app",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:security_group:my_app:my-app", Destination: "aws:vpc:my_app"},
+				},
+			},
+		},
+		{
+			name: "existing sg",
+			sg:   &SecurityGroup{Name: "my-app", ConstructsRef: initialRefs, Vpc: &Vpc{Name: "my_app"}},
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:security_group:my_app:my-app",
+					"aws:vpc:my_app",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:security_group:my_app:my-app", Destination: "aws:vpc:my_app"},
+				},
+			},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+			if tt.sg != nil {
+				dag.AddDependenciesReflect(tt.sg)
+			}
+			metadata := SecurityGroupCreateParams{
+				AppName: "my-app",
+				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+			}
+			sg := &SecurityGroup{}
+			err := sg.Create(dag, metadata)
+
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+
+			graphSG := dag.GetResourceByVertexId(sg.Id().String())
+			sg = graphSG.(*SecurityGroup)
+
+			assert.Equal(sg.Name, "my-app")
+			if tt.sg == nil {
+				assert.ElementsMatch(sg.ConstructsRef, metadata.Refs)
+			} else {
+				assert.ElementsMatch(sg.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+			}
+		})
+	}
+}
+
 func Test_GetSecurityGroup(t *testing.T) {
 	vpc := NewVpc("test")
 	type result struct {
diff --git a/pkg/provider/aws/resources/vpc_test.go b/pkg/provider/aws/resources/vpc_test.go
index a42dbdb9..74ce1a04 100644
--- a/pkg/provider/aws/resources/vpc_test.go
+++ b/pkg/provider/aws/resources/vpc_test.go
@@ -4,12 +4,522 @@ import (
 	"fmt"
 	"testing"
 
+	"github.com/klothoplatform/klotho/pkg/annotation"
 	"github.com/klothoplatform/klotho/pkg/config"
 	"github.com/klothoplatform/klotho/pkg/core"
 	"github.com/klothoplatform/klotho/pkg/core/coretesting"
 	"github.com/stretchr/testify/assert"
 )
 
+func Test_VpcCreate(t *testing.T) {
+	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	cases := []struct {
+		name string
+		vpc  *Vpc
+		want coretesting.ResourcesExpectation
+	}{
+		{
+			name: "nil vpc",
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:vpc:my_app",
+				},
+				Deps: []coretesting.StringDep{},
+			},
+		},
+		{
+			name: "existing vpc",
+			vpc:  &Vpc{Name: "my_app", ConstructsRef: initialRefs},
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:vpc:my_app",
+				},
+				Deps: []coretesting.StringDep{},
+			},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+			if tt.vpc != nil {
+				dag.AddResource(tt.vpc)
+			}
+			metadata := VpcCreateParams{
+				AppName: "my-app",
+				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+			}
+
+			vpc := &Vpc{}
+			err := vpc.Create(dag, metadata)
+
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+
+			graphVpc := dag.GetResourceByVertexId(vpc.Id().String())
+			vpc = graphVpc.(*Vpc)
+			assert.Equal(vpc.Name, "my_app")
+			if tt.vpc == nil {
+				assert.ElementsMatch(vpc.ConstructsRef, metadata.Refs)
+			} else {
+				assert.ElementsMatch(vpc.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+			}
+		})
+	}
+}
+
+func Test_ElasticIpCreate(t *testing.T) {
+	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	cases := []struct {
+		name string
+		eip  *ElasticIp
+		want coretesting.ResourcesExpectation
+	}{
+		{
+			name: "nil eip",
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:elastic_ip:my_app_ip0",
+				},
+				Deps: []coretesting.StringDep{},
+			},
+		},
+		{
+			name: "existing eip",
+			eip:  &ElasticIp{Name: "my_app_ip0", ConstructsRef: initialRefs},
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:elastic_ip:my_app_ip0",
+				},
+				Deps: []coretesting.StringDep{},
+			},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+			if tt.eip != nil {
+				dag.AddResource(tt.eip)
+			}
+			metadata := EipCreateParams{
+				AppName: "my-app",
+				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				IpName:  "ip0",
+			}
+
+			eip := &ElasticIp{}
+			err := eip.Create(dag, metadata)
+
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+
+			graphEip := dag.GetResourceByVertexId(eip.Id().String())
+			eip = graphEip.(*ElasticIp)
+			assert.Equal(eip.Name, "my_app_ip0")
+			if tt.eip == nil {
+				assert.ElementsMatch(eip.ConstructsRef, metadata.Refs)
+			} else {
+				assert.ElementsMatch(eip.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+			}
+		})
+	}
+}
+
+func Test_InternetGatewayCreate(t *testing.T) {
+	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	cases := []struct {
+		name string
+		igw  *InternetGateway
+		want coretesting.ResourcesExpectation
+	}{
+		{
+			name: "nil eip",
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:internet_gateway:my_app_igw",
+					"aws:vpc:my_app",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:internet_gateway:my_app_igw", Destination: "aws:vpc:my_app"},
+				},
+			},
+		},
+		{
+			name: "existing eip",
+			igw:  &InternetGateway{Name: "my_app_igw", ConstructsRef: initialRefs},
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:internet_gateway:my_app_igw",
+				},
+				Deps: []coretesting.StringDep{},
+			},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+			if tt.igw != nil {
+				dag.AddResource(tt.igw)
+			}
+			metadata := IgwCreateParams{
+				AppName: "my-app",
+				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+			}
+
+			igw := &InternetGateway{}
+			err := igw.Create(dag, metadata)
+
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+
+			graphIgw := dag.GetResourceByVertexId(igw.Id().String())
+			igw = graphIgw.(*InternetGateway)
+			assert.Equal(igw.Name, "my_app_igw")
+			if tt.igw == nil {
+				assert.NotNil(igw.Vpc)
+				assert.ElementsMatch(igw.ConstructsRef, metadata.Refs)
+			} else {
+				assert.ElementsMatch(igw.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+			}
+		})
+	}
+}
+
+func Test_NatGatewayCreate(t *testing.T) {
+	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	cases := []struct {
+		name string
+		nat  *NatGateway
+		want coretesting.ResourcesExpectation
+	}{
+		{
+			name: "nil nat",
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:availability_zones:AvailabilityZones",
+					"aws:elastic_ip:my_app_0",
+					"aws:internet_gateway:my_app_igw",
+					"aws:nat_gateway:my_app_0",
+					"aws:route_table:my_app_public",
+					"aws:vpc:my_app",
+					"aws:subnet_public:my_app:my_app_public0",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:internet_gateway:my_app_igw", Destination: "aws:vpc:my_app"},
+					{Source: "aws:nat_gateway:my_app_0", Destination: "aws:elastic_ip:my_app_0"},
+					{Source: "aws:nat_gateway:my_app_0", Destination: "aws:subnet_public:my_app:my_app_public0"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:internet_gateway:my_app_igw"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:subnet_public:my_app:my_app_public0"},
+					{Source: "aws:subnet_public:my_app:my_app_public0", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_public:my_app:my_app_public0", Destination: "aws:vpc:my_app"},
+				},
+			},
+		},
+		{
+			name: "existing nat",
+			nat:  &NatGateway{Name: "my_app_0", ConstructsRef: initialRefs},
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:nat_gateway:my_app_0",
+				},
+				Deps: []coretesting.StringDep{},
+			},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+			if tt.nat != nil {
+				dag.AddResource(tt.nat)
+			}
+			metadata := NatCreateParams{
+				AppName: "my-app",
+				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				AZ:      "0",
+			}
+
+			nat := &NatGateway{}
+			err := nat.Create(dag, metadata)
+
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+			graphNat := dag.GetResourceByVertexId(nat.Id().String())
+			nat = graphNat.(*NatGateway)
+
+			assert.Equal(nat.Name, "my_app_0")
+			if tt.nat == nil {
+				assert.NotNil(nat.Subnet)
+				assert.NotNil(nat.ElasticIp)
+				assert.ElementsMatch(nat.ConstructsRef, metadata.Refs)
+			} else {
+				assert.ElementsMatch(nat.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+			}
+		})
+	}
+}
+
+func Test_SubnetCreate(t *testing.T) {
+	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	cases := []struct {
+		name     string
+		subnet   *Subnet
+		addToDag bool
+		want     coretesting.ResourcesExpectation
+		wantErr  bool
+	}{
+		{
+			name:     "private subnet az0",
+			subnet:   &Subnet{Type: PrivateSubnet, AvailabilityZone: core.IaCValue{Property: "0"}, CidrBlock: "10.0.0.0/18"},
+			addToDag: false,
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:availability_zones:AvailabilityZones",
+					"aws:elastic_ip:my_app_0",
+					"aws:internet_gateway:my_app_igw",
+					"aws:nat_gateway:my_app_0",
+					"aws:route_table:my_app_private0",
+					"aws:route_table:my_app_public",
+					"aws:vpc:my_app",
+					"aws:subnet_private:my_app:my_app_private0",
+					"aws:subnet_public:my_app:my_app_public0",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:internet_gateway:my_app_igw", Destination: "aws:vpc:my_app"},
+					{Source: "aws:nat_gateway:my_app_0", Destination: "aws:elastic_ip:my_app_0"},
+					{Source: "aws:nat_gateway:my_app_0", Destination: "aws:subnet_public:my_app:my_app_public0"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:nat_gateway:my_app_0"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:internet_gateway:my_app_igw"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_private:my_app:my_app_private0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_private:my_app:my_app_private0", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_public:my_app:my_app_public0", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_public:my_app:my_app_public0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:subnet_public:my_app:my_app_public0"},
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:subnet_private:my_app:my_app_private0"},
+				},
+			},
+		},
+		{
+			name:     "private subnet az1",
+			subnet:   &Subnet{Type: PrivateSubnet, AvailabilityZone: core.IaCValue{Property: "1"}, CidrBlock: "10.0.64.0/18"},
+			addToDag: false,
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:availability_zones:AvailabilityZones",
+					"aws:elastic_ip:my_app_1",
+					"aws:internet_gateway:my_app_igw",
+					"aws:nat_gateway:my_app_1",
+					"aws:route_table:my_app_private1",
+					"aws:route_table:my_app_public",
+					"aws:vpc:my_app",
+					"aws:subnet_private:my_app:my_app_private1",
+					"aws:subnet_public:my_app:my_app_public1",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:internet_gateway:my_app_igw", Destination: "aws:vpc:my_app"},
+					{Source: "aws:nat_gateway:my_app_1", Destination: "aws:elastic_ip:my_app_1"},
+					{Source: "aws:nat_gateway:my_app_1", Destination: "aws:subnet_public:my_app:my_app_public1"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:nat_gateway:my_app_1"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:internet_gateway:my_app_igw"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_private:my_app:my_app_private1", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_private:my_app:my_app_private1", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_public:my_app:my_app_public1", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_public:my_app:my_app_public1", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:subnet_public:my_app:my_app_public1"},
+					{Source: "aws:route_table:my_app_private1", Destination: "aws:subnet_private:my_app:my_app_private1"},
+				},
+			},
+		},
+		{
+			name:     "public subnet az0",
+			subnet:   &Subnet{Type: PublicSubnet, AvailabilityZone: core.IaCValue{Property: "0"}, CidrBlock: "10.0.128.0/18"},
+			addToDag: false,
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:internet_gateway:my_app_igw",
+					"aws:availability_zones:AvailabilityZones",
+					"aws:route_table:my_app_public",
+					"aws:vpc:my_app",
+					"aws:subnet_public:my_app:my_app_public0",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:internet_gateway:my_app_igw", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:internet_gateway:my_app_igw"},
+					{Source: "aws:subnet_public:my_app:my_app_public0", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_public:my_app:my_app_public0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:subnet_public:my_app:my_app_public0"},
+				},
+			},
+		},
+		{
+			name:     "public subnet az1",
+			subnet:   &Subnet{Type: PublicSubnet, AvailabilityZone: core.IaCValue{Property: "1"}, CidrBlock: "10.0.192.0/18"},
+			addToDag: false,
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:internet_gateway:my_app_igw",
+					"aws:route_table:my_app_public",
+					"aws:availability_zones:AvailabilityZones",
+					"aws:vpc:my_app",
+					"aws:subnet_public:my_app:my_app_public1",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:internet_gateway:my_app_igw", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:internet_gateway:my_app_igw"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_public:my_app:my_app_public1", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_public:my_app:my_app_public1", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:subnet_public:my_app:my_app_public1"},
+				},
+			},
+		},
+		{
+			name:     "existing subnet",
+			subnet:   &Subnet{Name: "my_app_public0", Type: PublicSubnet, AvailabilityZone: core.IaCValue{Property: "0"}, ConstructsRef: initialRefs, Vpc: &Vpc{Name: "my_app"}, CidrBlock: "10.0.128.0/18"},
+			addToDag: true,
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:availability_zones:AvailabilityZones",
+					"aws:internet_gateway:my_app_igw",
+					"aws:route_table:my_app_public",
+					"aws:subnet_public:my_app:my_app_public0",
+					"aws:vpc:my_app",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:internet_gateway:my_app_igw", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:internet_gateway:my_app_igw"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:vpc:my_app"},
+					{Source: "aws:subnet_public:my_app:my_app_public0", Destination: "aws:availability_zones:AvailabilityZones"},
+					{Source: "aws:subnet_public:my_app:my_app_public0", Destination: "aws:vpc:my_app"},
+					{Source: "aws:route_table:my_app_public", Destination: "aws:subnet_public:my_app:my_app_public0"},
+				},
+			},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+			if tt.addToDag {
+				dag.AddResource(tt.subnet)
+			}
+			metadata := SubnetCreateParams{
+				AppName: "my-app",
+				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				AZ:      tt.subnet.AvailabilityZone.Property,
+				Type:    tt.subnet.Type,
+			}
+			subnet := &Subnet{}
+			err := subnet.Create(dag, metadata)
+
+			if tt.wantErr {
+				assert.Error(err)
+				return
+			}
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+
+			graphSubnet := dag.GetResourceByVertexId(subnet.Id().String())
+			subnet = graphSubnet.(*Subnet)
+
+			assert.Equal(subnet.Name, fmt.Sprintf("my_app_%s%s", tt.subnet.Type, tt.subnet.AvailabilityZone.Property))
+			assert.Equal(subnet.Type, tt.subnet.Type)
+			assert.Equal(subnet.AvailabilityZone.Property, tt.subnet.AvailabilityZone.Property)
+			if tt.addToDag == false {
+				assert.ElementsMatch(subnet.ConstructsRef, metadata.Refs)
+			} else {
+				assert.ElementsMatch(subnet.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+			}
+		})
+	}
+}
+
+func Test_RouteTableCreate(t *testing.T) {
+	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	cases := []struct {
+		name string
+		rt   *RouteTable
+		want coretesting.ResourcesExpectation
+	}{
+		{
+			name: "nil route table ",
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:route_table:my_app_private0",
+					"aws:vpc:my_app",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:vpc:my_app"},
+				},
+			},
+		},
+		{
+			name: "existing rt",
+			rt:   &RouteTable{Name: "my_app_private0", ConstructsRef: initialRefs},
+			want: coretesting.ResourcesExpectation{
+				Nodes: []string{
+					"aws:route_table:my_app_private0",
+					"aws:vpc:my_app",
+				},
+				Deps: []coretesting.StringDep{
+					{Source: "aws:route_table:my_app_private0", Destination: "aws:vpc:my_app"},
+				},
+			},
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+			dag := core.NewResourceGraph()
+			if tt.rt != nil {
+				dag.AddResource(tt.rt)
+			}
+			metadata := RouteTableCreateParams{
+				AppName: "my-app",
+				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Name:    "private0",
+			}
+
+			rt := &RouteTable{}
+			err := rt.Create(dag, metadata)
+
+			if !assert.NoError(err) {
+				return
+			}
+			tt.want.Assert(t, dag)
+
+			graphRT := dag.GetResourceByVertexId(rt.Id().String())
+			rt = graphRT.(*RouteTable)
+
+			assert.Equal(rt.Name, "my_app_private0")
+			if tt.rt == nil {
+				assert.NotNil(rt.Vpc)
+				assert.ElementsMatch(rt.ConstructsRef, metadata.Refs)
+			} else {
+				assert.ElementsMatch(rt.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+			}
+		})
+	}
+}
+
 func Test_CreateNetwork(t *testing.T) {
 	appName := "test-app"
 	cases := []struct {