Repository: klothoplatform/klotho
Pull request #637 (2023-05-25 16:54:01)

Standardize Resource Graph APIs
* Remove vertex IDs from resource graph API
* Add API to resource graph for reverse topological ordering

### Standard checks

- **Unit tests**: None new required
- **Docs**: n/a - internal
- **Backwards compatibility**: purely internal



Patch:
diff --git a/pkg/core/annotation.go b/pkg/core/annotation.go
index df4db772..6c28b4ca 100644
--- a/pkg/core/annotation.go
+++ b/pkg/core/annotation.go
@@ -28,6 +28,8 @@ type (
 	}
 
 	AnnotationMap map[AnnotationKey]*Annotation
+
+	AnnotationKeySet map[AnnotationKey]struct{}
 )
 
 var (
@@ -133,3 +135,56 @@ func (m AnnotationMap) InSourceOrder() []*Annotation {
 
 	return list
 }
+
+func (s *AnnotationKeySet) Add(k AnnotationKey) {
+	if *s == nil {
+		*s = make(AnnotationKeySet)
+	}
+	(*s)[k] = struct{}{}
+}
+
+func (s AnnotationKeySet) Has(k AnnotationKey) bool {
+	_, ok := s[k]
+	return ok
+}
+
+func (s AnnotationKeySet) Delete(k AnnotationKey) {
+	delete(s, k)
+}
+
+func (s *AnnotationKeySet) AddAll(ks AnnotationKeySet) {
+	for k := range ks {
+		s.Add(k)
+	}
+}
+
+func (s AnnotationKeySet) GetSingle() (AnnotationKey, bool) {
+	if len(s) != 1 {
+		return AnnotationKey{}, false
+	}
+	for k := range s {
+		return k, true
+	}
+	return AnnotationKey{}, false
+}
+
+func (s AnnotationKeySet) Clone() AnnotationKeySet {
+	clone := make(AnnotationKeySet)
+	clone.AddAll(s)
+	return clone
+}
+
+func (s AnnotationKeySet) CloneWith(ks AnnotationKeySet) AnnotationKeySet {
+	clone := make(AnnotationKeySet)
+	clone.AddAll(s)
+	clone.AddAll(ks)
+	return clone
+}
+
+func AnnotationKeySetOf(keys ...AnnotationKey) AnnotationKeySet {
+	s := make(AnnotationKeySet)
+	for _, k := range keys {
+		s.Add(k)
+	}
+	return s
+}
diff --git a/pkg/core/resource_graph.go b/pkg/core/resource_graph.go
index 6e796b39..75931f76 100644
--- a/pkg/core/resource_graph.go
+++ b/pkg/core/resource_graph.go
@@ -50,26 +50,15 @@ 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())
-	}
+	rg.AddDependencyWithData(deployedSecond, deployedFirst, nil)
 }
 
 // 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)
-	}
+	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 {
@@ -88,20 +77,12 @@ func GetResource[T Resource](g *ResourceGraph, id ResourceId) (resource T, ok bo
 	return
 }
 
-func (rg *ResourceGraph) GetResourceByVertexId(id string) Resource {
-	return rg.underlying.GetVertex(id)
-}
-
-func (rg *ResourceGraph) GetDependency(source Resource, target Resource) *graph.Edge[Resource] {
-	return rg.underlying.GetEdge(rg.underlying.IdForNode(source), rg.underlying.IdForNode(target))
+func (rg *ResourceGraph) GetDependency(source ResourceId, target ResourceId) *graph.Edge[Resource] {
+	return rg.underlying.GetEdge(source.String(), target.String())
 }
 
-func (rg *ResourceGraph) GetDependencyByVertexIds(source string, target string) *graph.Edge[Resource] {
-	return rg.underlying.GetEdge(source, target)
-}
-
-func (rg *ResourceGraph) RemoveDependency(source string, target string) error {
-	return rg.underlying.RemoveEdge(source, target)
+func (rg *ResourceGraph) RemoveDependency(source ResourceId, target ResourceId) error {
+	return rg.underlying.RemoveEdge(source.String(), target.String())
 }
 
 func (rg *ResourceGraph) ListResources() []Resource {
@@ -112,10 +93,6 @@ func (rg *ResourceGraph) ListDependencies() []graph.Edge[Resource] {
 	return rg.underlying.GetAllEdges()
 }
 
-func (rg *ResourceGraph) VertexIdsInTopologicalOrder() ([]string, error) {
-	return rg.underlying.VertexIdsInTopologicalOrder()
-}
-
 func (rg *ResourceGraph) GetDownstreamDependencies(source Resource) []graph.Edge[Resource] {
 	return rg.underlying.OutgoingEdges(source)
 }
@@ -132,8 +109,29 @@ func (rg *ResourceGraph) GetUpstreamResources(source Resource) []Resource {
 	return rg.underlying.IncomingVertices(source)
 }
 
-func (rg *ResourceGraph) TopologicalSort() ([]string, error) {
-	return rg.underlying.VertexIdsInTopologicalOrder()
+func (rg *ResourceGraph) TopologicalSort() ([]Resource, error) {
+	ids, err := rg.underlying.VertexIdsInTopologicalOrder()
+	if err != nil {
+		return nil, err
+	}
+	resources := make([]Resource, len(ids))
+	for i, id := range ids {
+		resources[i] = rg.underlying.GetVertex(id)
+	}
+	return resources, nil
+}
+
+func (rg *ResourceGraph) ReverseTopologicalSort() ([]Resource, error) {
+	ids, err := rg.underlying.VertexIdsInTopologicalOrder()
+	if err != nil {
+		return nil, err
+	}
+	total := len(ids)
+	resources := make([]Resource, total)
+	for i := total; i > 0; i-- {
+		resources[total-i] = rg.underlying.GetVertex(ids[i-1])
+	}
+	return resources, nil
 }
 
 // AddDependenciesReflect uses reflection to inspect the fields of the resource given
@@ -154,7 +152,6 @@ func (rg *ResourceGraph) TopologicalSort() ([]string, error) {
 // - `NestedSpecificDepArray []*K`
 // - `NestedDependencyMap  map[string]K`
 // - `NestedSpecificDepMap map[string]*K`
-
 func (rg *ResourceGraph) AddDependenciesReflect(source Resource) {
 	rg.AddResource(source)
 
@@ -234,7 +231,7 @@ func (rg *ResourceGraph) addDependenciesReflect(source Resource, targetValue ref
 
 func (rg *ResourceGraph) GetAllUpstreamResources(source Resource) []Resource {
 	var upstreams []Resource
-	upstreamsSet := map[Resource]struct{}{}
+	upstreamsSet := make(map[Resource]struct{})
 	for r := range rg.getAllUpstreamResourcesSet(source, upstreamsSet) {
 		upstreams = append(upstreams, r)
 	}
@@ -321,7 +318,6 @@ func (rg *ResourceGraph) CreateDependencies(res Resource, params map[string]any)
 //
 // 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 {
@@ -333,7 +329,7 @@ func CreateResource[T Resource](rg *ResourceGraph, params any) (resource T, err
 		return
 	}
 
-	currValue := rg.GetResourceByVertexId(castedRes.Id().String())
+	currValue := rg.GetResource(castedRes.Id())
 	if currValue != nil {
 		return currValue.(T), nil
 	}
@@ -347,7 +343,7 @@ func (rg *ResourceGraph) actOnValue(targetValue reflect.Value, res Resource, met
 			value = reflect.New(targetValue.Type().Elem()).Interface().(Resource)
 		}
 		err := rg.callCreate(reflect.ValueOf(value), metadata)
-		currValue := rg.GetResourceByVertexId(value.Id().String())
+		currValue := rg.GetResource(value.Id())
 		if currValue != nil {
 			value = currValue
 		}
@@ -363,7 +359,7 @@ func (rg *ResourceGraph) actOnValue(targetValue reflect.Value, res Resource, met
 	case *IaCValue:
 		if value != nil && value.Resource != nil {
 			err := rg.callCreate(reflect.ValueOf(value.Resource), metadata)
-			currValue := rg.GetResourceByVertexId(value.Resource.Id().String())
+			currValue := rg.GetResource(value.Resource.Id())
 			if currValue != nil {
 				value.Resource = currValue
 			}
@@ -380,7 +376,7 @@ func (rg *ResourceGraph) actOnValue(targetValue reflect.Value, res Resource, met
 	case IaCValue:
 		if value.Resource != nil {
 			err := rg.callCreate(reflect.ValueOf(value.Resource), metadata)
-			currValue := rg.GetResourceByVertexId(value.Resource.Id().String())
+			currValue := rg.GetResource(value.Resource.Id())
 			if currValue != nil {
 				value.Resource = currValue
 			}
@@ -436,7 +432,6 @@ func (rg *ResourceGraph) callCreate(targetValue reflect.Value, metadata any) err
 
 // 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())
 	}
diff --git a/pkg/core/resources.go b/pkg/core/resources.go
index f5a549f6..af33314f 100644
--- a/pkg/core/resources.go
+++ b/pkg/core/resources.go
@@ -20,7 +20,7 @@ type (
 	// Resource describes a resource at the provider, infrastructure level
 	Resource interface {
 		// KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-		KlothoConstructRef() []AnnotationKey
+		KlothoConstructRef() AnnotationKeySet
 		// Id returns the id of the cloud resource
 		Id() ResourceId
 	}
diff --git a/pkg/infra/iac2/resources.go b/pkg/infra/iac2/resources.go
index 75cb4b4b..6714fc19 100644
--- a/pkg/infra/iac2/resources.go
+++ b/pkg/infra/iac2/resources.go
@@ -7,7 +7,7 @@ import (
 
 type (
 	KubernetesProvider struct {
-		ConstructsRef         []core.AnnotationKey
+		ConstructsRef         core.AnnotationKeySet
 		KubeConfig            core.Resource
 		Name                  string
 		EnableServerSideApply bool
@@ -15,13 +15,13 @@ type (
 
 	RouteTableAssociation struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		Subnet        *resources.Subnet
 		RouteTable    *resources.RouteTable
 	}
 
 	SecurityGroupRule struct {
-		ConstructsRef   []core.AnnotationKey
+		ConstructsRef   core.AnnotationKeySet
 		Name            string
 		Description     string
 		FromPort        int
@@ -33,7 +33,7 @@ type (
 	}
 )
 
-func (e *KubernetesProvider) KlothoConstructRef() []core.AnnotationKey {
+func (e *KubernetesProvider) KlothoConstructRef() core.AnnotationKeySet {
 	return e.ConstructsRef
 }
 
@@ -45,7 +45,7 @@ func (e *KubernetesProvider) Id() core.ResourceId {
 	}
 }
 
-func (e *RouteTableAssociation) KlothoConstructRef() []core.AnnotationKey {
+func (e *RouteTableAssociation) KlothoConstructRef() core.AnnotationKeySet {
 	return e.ConstructsRef
 }
 
@@ -70,7 +70,7 @@ type (
 )
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (role *RolePolicyAttachment) KlothoConstructRef() []core.AnnotationKey {
+func (role *RolePolicyAttachment) KlothoConstructRef() core.AnnotationKeySet {
 	return nil
 }
 
@@ -83,7 +83,7 @@ func (role *RolePolicyAttachment) Id() core.ResourceId {
 	}
 }
 
-func (e *SecurityGroupRule) KlothoConstructRef() []core.AnnotationKey {
+func (e *SecurityGroupRule) KlothoConstructRef() core.AnnotationKeySet {
 	return e.ConstructsRef
 }
 
diff --git a/pkg/infra/iac2/templates_compiler.go b/pkg/infra/iac2/templates_compiler.go
index 2f1ffeb7..5abd0295 100644
--- a/pkg/infra/iac2/templates_compiler.go
+++ b/pkg/infra/iac2/templates_compiler.go
@@ -104,13 +104,11 @@ func standardTemplatesProvider() *templatesProvider {
 
 func (tc TemplatesCompiler) RenderBody(out io.Writer) error {
 	errs := multierr.Error{}
-	vertexIds, err := tc.resourceGraph.VertexIdsInTopologicalOrder()
+	res, err := tc.resourceGraph.ReverseTopologicalSort()
 	if err != nil {
 		return err
 	}
-	for i := len(vertexIds) - 1; i >= 0; i-- {
-		id := vertexIds[i]
-		resource := tc.resourceGraph.GetResourceByVertexId(id)
+	for i, resource := range res {
 		switch resource.(type) {
 		case *resources.AccountId, *resources.Region:
 			continue // skip resources that we know are rendered outside of the body
@@ -121,7 +119,7 @@ func (tc TemplatesCompiler) RenderBody(out io.Writer) error {
 		}
 		err := tc.renderResource(out, resource)
 		errs.Append(err)
-		if i > 0 {
+		if i < len(res)-1 {
 			_, err = out.Write([]byte("\n\n"))
 			if err != nil {
 				return err
diff --git a/pkg/infra/kubernetes/helm_chart.go b/pkg/infra/kubernetes/helm_chart.go
index 6f409c8c..a56d6a9f 100644
--- a/pkg/infra/kubernetes/helm_chart.go
+++ b/pkg/infra/kubernetes/helm_chart.go
@@ -28,7 +28,7 @@ type HelmChart struct {
 	Files          []core.File
 	ProviderValues []HelmChartValue
 
-	ConstructRefs    []core.AnnotationKey
+	ConstructRefs    core.AnnotationKeySet
 	ClustersProvider core.IaCValue
 	Repo             string
 	Version          string
@@ -37,7 +37,7 @@ type HelmChart struct {
 }
 
 // KlothoConstructRef returns a slice containing the ids of any Klotho constructs is correlated to
-func (chart *HelmChart) KlothoConstructRef() []core.AnnotationKey { return chart.ConstructRefs }
+func (chart *HelmChart) KlothoConstructRef() core.AnnotationKeySet { return chart.ConstructRefs }
 
 func (chart *HelmChart) Id() core.ResourceId {
 	return core.ResourceId{
diff --git a/pkg/infra/kubernetes/kubeconfig.go b/pkg/infra/kubernetes/kubeconfig.go
index ef70984e..8dc0dbbd 100644
--- a/pkg/infra/kubernetes/kubeconfig.go
+++ b/pkg/infra/kubernetes/kubeconfig.go
@@ -10,7 +10,7 @@ const (
 
 type (
 	Kubeconfig struct {
-		ConstructsRef  []core.AnnotationKey
+		ConstructsRef  core.AnnotationKeySet
 		Name           string
 		ApiVersion     string
 		Kind           string
@@ -51,7 +51,7 @@ type (
 	}
 )
 
-func (k Kubeconfig) KlothoConstructRef() []core.AnnotationKey { return k.ConstructsRef }
+func (k Kubeconfig) KlothoConstructRef() core.AnnotationKeySet { return k.ConstructsRef }
 
 func (k Kubeconfig) Id() core.ResourceId {
 	return core.ResourceId{
diff --git a/pkg/infra/kubernetes/kustomize.go b/pkg/infra/kubernetes/kustomize.go
index c443a097..4d098a25 100644
--- a/pkg/infra/kubernetes/kustomize.go
+++ b/pkg/infra/kubernetes/kustomize.go
@@ -11,14 +11,14 @@ const (
 type (
 	KustomizeDirectory struct {
 		Name             string
-		ConstructRefs    []core.AnnotationKey
+		ConstructRefs    core.AnnotationKeySet
 		Directory        string
 		ClustersProvider core.IaCValue
 	}
 )
 
 // KlothoConstructRef returns a slice containing the ids of any Klotho constructs is correlated to
-func (dir *KustomizeDirectory) KlothoConstructRef() []core.AnnotationKey { return dir.ConstructRefs }
+func (dir *KustomizeDirectory) KlothoConstructRef() core.AnnotationKeySet { return dir.ConstructRefs }
 
 func (dir *KustomizeDirectory) Id() core.ResourceId {
 	return core.ResourceId{
diff --git a/pkg/infra/kubernetes/manifest.go b/pkg/infra/kubernetes/manifest.go
index 9204ed76..18b40f1d 100644
--- a/pkg/infra/kubernetes/manifest.go
+++ b/pkg/infra/kubernetes/manifest.go
@@ -28,7 +28,7 @@ const (
 type (
 	Manifest struct {
 		Name             string
-		ConstructRefs    []core.AnnotationKey
+		ConstructRefs    core.AnnotationKeySet
 		FilePath         string
 		Transformations  map[string]core.IaCValue
 		ClustersProvider core.IaCValue
@@ -46,7 +46,7 @@ type (
 )
 
 // KlothoConstructRef returns a slice containing the ids of any Klotho constructs is correlated to
-func (manifest *Manifest) KlothoConstructRef() []core.AnnotationKey { return manifest.ConstructRefs }
+func (manifest *Manifest) KlothoConstructRef() core.AnnotationKeySet { return manifest.ConstructRefs }
 
 func (manifest *Manifest) Id() core.ResourceId {
 	return core.ResourceId{
diff --git a/pkg/infra/kubernetes/plugin.go b/pkg/infra/kubernetes/plugin.go
index 47dd6e58..e7d7f784 100644
--- a/pkg/infra/kubernetes/plugin.go
+++ b/pkg/infra/kubernetes/plugin.go
@@ -184,9 +184,10 @@ func (p *Kubernetes) getKlothoCharts(constructGraph *core.ConstructGraph) (map[s
 				khChart = &HelmChart{
 					ValuesFiles:   valuesFiles,
 					Directory:     chartDir,
-					ConstructRefs: []core.AnnotationKey{unit.Provenance()},
+					ConstructRefs: make(core.AnnotationKeySet),
 					Values:        make(map[string]any),
 				}
+				khChart.ConstructRefs.Add(unit.Provenance())
 				klothoCharts[chartDir] = khChart
 			} else {
 				foundDifference := false
@@ -208,7 +209,7 @@ func (p *Kubernetes) getKlothoCharts(constructGraph *core.ConstructGraph) (map[s
 			}
 
 			khChart.ExecutionUnits = append(khChart.ExecutionUnits, &HelmExecUnit{Name: unit.ID, Namespace: "default"})
-			khChart.ConstructRefs = append(khChart.ConstructRefs, unit.AnnotationKey)
+			khChart.ConstructRefs.Add(unit.AnnotationKey)
 		}
 	}
 	return klothoCharts, errs.ErrOrNil()
diff --git a/pkg/knowledge_base/edge_kb.go b/pkg/knowledge_base/edge_kb.go
index e4734c2d..c3db8630 100644
--- a/pkg/knowledge_base/edge_kb.go
+++ b/pkg/knowledge_base/edge_kb.go
@@ -230,7 +230,7 @@ func (kb EdgeKB) ExpandEdges(dag *core.ResourceGraph, appName string) (err error
 			// 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) > 1 {
 				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())
+				err := dag.RemoveDependency(dep.Source.Id(), dep.Destination.Id())
 				if err != nil {
 					merr.Append(err)
 					continue
diff --git a/pkg/provider/aws/content_delivery_network.go b/pkg/provider/aws/content_delivery_network.go
index 78e81c03..d182e390 100644
--- a/pkg/provider/aws/content_delivery_network.go
+++ b/pkg/provider/aws/content_delivery_network.go
@@ -59,7 +59,7 @@ func (a *AWS) createCDNs(result *core.ConstructGraph, dag *core.ResourceGraph) e
 				dag.AddDependency(distro, apiStage)
 				distro.DefaultCacheBehavior.DefaultTtl = 0
 				resources.CreateCustomOrigin(construct, apiStage, distro)
-				distro.ConstructsRef = append(distro.ConstructsRef, construct.Provenance())
+				distro.ConstructsRef.Add(construct.Provenance())
 			case *core.StaticUnit:
 				res, found := a.GetResourcesDirectlyTiedToConstruct(construct)
 				if !found {
@@ -77,7 +77,7 @@ func (a *AWS) createCDNs(result *core.ConstructGraph, dag *core.ResourceGraph) e
 				dag.AddDependency(distro, bucket)
 				distro.DefaultRootObject = bucket.IndexDocument
 				resources.CreateS3Origin(construct, bucket, distro, dag)
-				distro.ConstructsRef = append(distro.ConstructsRef, construct.Provenance())
+				distro.ConstructsRef.Add(construct.Provenance())
 			}
 		}
 		dag.AddDependenciesReflect(distro)
diff --git a/pkg/provider/aws/execution_unit.go b/pkg/provider/aws/execution_unit.go
index 2468933e..5beb2518 100644
--- a/pkg/provider/aws/execution_unit.go
+++ b/pkg/provider/aws/execution_unit.go
@@ -16,7 +16,7 @@ func (a *AWS) expandExecutionUnit(dag *core.ResourceGraph, unit *core.ExecutionU
 	case Lambda:
 		lambda, err := core.CreateResource[*resources.LambdaFunction](dag, resources.LambdaCreateParams{
 			AppName: a.Config.AppName,
-			Refs:    []core.AnnotationKey{unit.AnnotationKey},
+			Refs:    core.AnnotationKeySetOf(unit.AnnotationKey),
 			Name:    unit.ID,
 		})
 		if err != nil {
@@ -38,7 +38,7 @@ func (a *AWS) expandExecutionUnit(dag *core.ResourceGraph, unit *core.ExecutionU
 			err := fargateProfile.Create(dag, resources.EksFargateProfileCreateParams{
 				Name:        "klotho-fargate-profile",
 				ClusterName: clusterName,
-				Refs:        []core.AnnotationKey{unit.AnnotationKey},
+				Refs:        core.AnnotationKeySetOf(unit.AnnotationKey),
 				AppName:     a.Config.AppName,
 				NetworkType: a.Config.GetExecutionUnit(unit.ID).NetworkPlacement,
 			})
@@ -56,7 +56,7 @@ func (a *AWS) expandExecutionUnit(dag *core.ResourceGraph, unit *core.ExecutionU
 		}
 		subParams := map[string]any{
 			"ClustersProvider": resources.EksClusterCreateParams{
-				Refs:    []core.AnnotationKey{unit.AnnotationKey},
+				Refs:    core.AnnotationKeySetOf(unit.AnnotationKey),
 				AppName: a.Config.AppName,
 				Name:    clusterName,
 			},
@@ -101,7 +101,7 @@ func (a *AWS) handleHelmChartAwsValues(chart *kubernetes.HelmChart, unit *core.E
 			}
 			valueParams[val.Key] = resources.ImageCreateParams{
 				AppName: a.Config.AppName,
-				Refs:    []core.AnnotationKey{unit.AnnotationKey},
+				Refs:    core.AnnotationKeySetOf(unit.AnnotationKey),
 				Name:    unit.ID,
 			}
 		case kubernetes.ServiceAccountAnnotationTransformation:
@@ -111,7 +111,7 @@ func (a *AWS) handleHelmChartAwsValues(chart *kubernetes.HelmChart, unit *core.E
 			}
 			valueParams[val.Key] = resources.RoleCreateParams{
 				Name:    fmt.Sprintf("%s-%s-ExecutionRole", a.Config.AppName, unit.ID),
-				Refs:    []core.AnnotationKey{unit.AnnotationKey},
+				Refs:    core.AnnotationKeySetOf(unit.AnnotationKey),
 				AppName: a.Config.AppName,
 			}
 		case kubernetes.InstanceTypeKey:
@@ -128,7 +128,7 @@ func (a *AWS) handleHelmChartAwsValues(chart *kubernetes.HelmChart, unit *core.E
 				NetworkType:  a.Config.GetExecutionUnit(unit.ID).NetworkPlacement,
 				AppName:      a.Config.AppName,
 				ClusterName:  clusterName,
-				Refs:         []core.AnnotationKey{unit.AnnotationKey},
+				Refs:         core.AnnotationKeySetOf(unit.AnnotationKey),
 			}
 		case kubernetes.TargetGroupTransformation:
 		}
@@ -136,14 +136,15 @@ func (a *AWS) handleHelmChartAwsValues(chart *kubernetes.HelmChart, unit *core.E
 	return
 }
 
-func (a *AWS) getLambdaConfiguration(result *core.ConstructGraph, dag *core.ResourceGraph, refs []core.AnnotationKey) (resources.LambdaFunctionConfigureParams, error) {
-	if len(refs) != 1 {
+func (a *AWS) getLambdaConfiguration(result *core.ConstructGraph, dag *core.ResourceGraph, refs core.AnnotationKeySet) (resources.LambdaFunctionConfigureParams, error) {
+	ref, oneRef := refs.GetSingle()
+	if !oneRef {
 		return resources.LambdaFunctionConfigureParams{}, fmt.Errorf("lambda must only have one construct reference")
 	}
 	lambdaConfig := resources.LambdaFunctionConfigureParams{}
-	construct := result.GetConstruct(refs[0].ToId())
+	construct := result.GetConstruct(ref.ToId())
 	if construct == nil {
-		return resources.LambdaFunctionConfigureParams{}, fmt.Errorf("construct with id %s does not exist", refs[0].ToId())
+		return resources.LambdaFunctionConfigureParams{}, fmt.Errorf("construct with id %s does not exist", ref.ToId())
 	}
 	unit, ok := construct.(*core.ExecutionUnit)
 	if !ok {
@@ -154,20 +155,21 @@ func (a *AWS) getLambdaConfiguration(result *core.ConstructGraph, dag *core.Reso
 			lambdaConfig.EnvironmentVariables = append(lambdaConfig.EnvironmentVariables, env)
 		}
 	}
-	cfg := config.ConvertFromInfraParams[config.ServerlessTypeParams](a.Config.GetExecutionUnit(refs[0].ID).InfraParams)
+	cfg := config.ConvertFromInfraParams[config.ServerlessTypeParams](a.Config.GetExecutionUnit(ref.ID).InfraParams)
 	lambdaConfig.MemorySize = cfg.Memory
 	lambdaConfig.Timeout = cfg.Timeout
 	return lambdaConfig, nil
 }
 
-func (a *AWS) getImageConfiguration(result *core.ConstructGraph, dag *core.ResourceGraph, refs []core.AnnotationKey) (resources.EcrImageConfigureParams, error) {
-	if len(refs) != 1 {
+func (a *AWS) getImageConfiguration(result *core.ConstructGraph, dag *core.ResourceGraph, refs core.AnnotationKeySet) (resources.EcrImageConfigureParams, error) {
+	ref, oneRef := refs.GetSingle()
+	if !oneRef {
 		return resources.EcrImageConfigureParams{}, fmt.Errorf("image must only have one construct reference but got %d: %v", len(refs), refs)
 	}
 	imageConfig := resources.EcrImageConfigureParams{}
-	construct := result.GetConstruct(refs[0].ToId())
+	construct := result.GetConstruct(ref.ToId())
 	if construct == nil {
-		return resources.EcrImageConfigureParams{}, fmt.Errorf("construct with id %s does not exist", refs[0].ToId())
+		return resources.EcrImageConfigureParams{}, fmt.Errorf("construct with id %s does not exist", ref.ToId())
 	}
 	unit, ok := construct.(*core.ExecutionUnit)
 	if !ok {
@@ -178,10 +180,10 @@ func (a *AWS) getImageConfiguration(result *core.ConstructGraph, dag *core.Resou
 	return imageConfig, nil
 }
 
-func (a *AWS) getNodeGroupConfiguration(result *core.ConstructGraph, dag *core.ResourceGraph, refs []core.AnnotationKey) (resources.EksNodeGroupConfigureParams, error) {
+func (a *AWS) getNodeGroupConfiguration(result *core.ConstructGraph, dag *core.ResourceGraph, refs core.AnnotationKeySet) (resources.EksNodeGroupConfigureParams, error) {
 	nodeGroupConfig := resources.EksNodeGroupConfigureParams{}
 	nodeGroupConfig.DiskSize = 20
-	for _, ref := range refs {
+	for ref := range refs {
 		construct := result.GetConstruct(ref.ToId())
 		unit, ok := construct.(*core.ExecutionUnit)
 		if !ok {
@@ -230,18 +232,18 @@ func (a *AWS) handleExecUnitProxy(result *core.ConstructGraph, dag *core.Resourc
 						dag.AddResource(execPolicy)
 					}
 					// We do not add the policy to the units list in policy generator otherwise we will cause a circular dependency
-					execPolicy.ConstructsRef = append(execPolicy.ConstructsRef, unit.AnnotationKey)
+					execPolicy.ConstructsRef.Add(unit.AnnotationKey)
 					dag.AddDependency(a.PolicyGenerator.GetUnitRole(unit.Id()), execPolicy)
 					dag.AddDependency(execPolicy, targetLambda)
 				case kubernetes.KubernetesType:
-					privateNamespace := resources.NewPrivateDnsNamespace(a.Config.AppName, []core.AnnotationKey{unit.AnnotationKey}, resources.GetVpc(a.Config, dag))
+					privateNamespace := resources.NewPrivateDnsNamespace(a.Config.AppName, core.AnnotationKeySetOf(unit.AnnotationKey), resources.GetVpc(a.Config, dag))
 					if ns := dag.GetResource(privateNamespace.Id()); ns != nil {
 						namespace, ok := ns.(*resources.PrivateDnsNamespace)
 						if !ok {
 							return errors.Errorf("Found a non PrivateDnsNamespace with same id as global PrivateDnsNamespace, %s", namespace.Id())
 						}
 						privateNamespace = namespace
-						privateNamespace.ConstructsRef = append(privateNamespace.ConstructsRef, unit.Provenance())
+						privateNamespace.ConstructsRef.Add(unit.Provenance())
 					} else {
 						dag.AddDependenciesReflect(privateNamespace)
 					}
@@ -256,7 +258,7 @@ func (a *AWS) handleExecUnitProxy(result *core.ConstructGraph, dag *core.Resourc
 					serviceDiscoveryPolicyDoc := resources.CreateAllowPolicyDocument([]string{"servicediscovery:DiscoverInstances"}, []core.IaCValue{{Property: core.ALL_RESOURCES_IAC_VALUE}})
 					execPolicy := resources.NewIamPolicy(a.Config.AppName, fmt.Sprintf("%s-servicediscovery", privateNamespace.Name), unit.AnnotationKey, serviceDiscoveryPolicyDoc)
 					dag.AddResource(execPolicy)
-					execPolicy.ConstructsRef = append(execPolicy.ConstructsRef, unit.AnnotationKey)
+					execPolicy.ConstructsRef.Add(unit.AnnotationKey)
 					dag.AddDependency(a.PolicyGenerator.GetUnitRole(unit.Id()), execPolicy)
 
 					cluster, err := findUnitsCluster(targetUnit, dag)
@@ -282,10 +284,8 @@ func (a *AWS) handleExecUnitProxy(result *core.ConstructGraph, dag *core.Resourc
 func findUnitsCluster(unit *core.ExecutionUnit, dag *core.ResourceGraph) (*resources.EksCluster, error) {
 	for _, res := range dag.ListResources() {
 		if r, ok := res.(*resources.EksCluster); ok {
-			for _, ref := range r.ConstructsRef {
-				if ref == unit.Provenance() {
-					return r, nil
-				}
+			if r.ConstructsRef.Has(unit.Provenance()) {
+				return r, nil
 			}
 		}
 	}
diff --git a/pkg/provider/aws/expose.go b/pkg/provider/aws/expose.go
index 4bd12dd3..8012aa5a 100644
--- a/pkg/provider/aws/expose.go
+++ b/pkg/provider/aws/expose.go
@@ -15,7 +15,7 @@ func (a *AWS) expandExpose(dag *core.ResourceGraph, expose *core.Gateway) error
 	case ApiGateway:
 		stage, err := core.CreateResource[*resources.ApiStage](dag, resources.ApiStageCreateParams{
 			AppName: a.Config.AppName,
-			Refs:    []core.AnnotationKey{expose.AnnotationKey},
+			Refs:    core.AnnotationKeySetOf(expose.AnnotationKey),
 			Name:    expose.ID,
 		})
 		if err != nil {
diff --git a/pkg/provider/aws/fs.go b/pkg/provider/aws/fs.go
index 8535e8e7..a4c447c8 100644
--- a/pkg/provider/aws/fs.go
+++ b/pkg/provider/aws/fs.go
@@ -8,7 +8,7 @@ import (
 func (a *AWS) expandFs(dag *core.ResourceGraph, fs *core.Fs) error {
 	bucket, err := core.CreateResource[*resources.S3Bucket](dag, resources.S3BucketCreateParams{
 		AppName: a.Config.AppName,
-		Refs:    []core.AnnotationKey{fs.AnnotationKey},
+		Refs:    core.AnnotationKeySetOf(fs.AnnotationKey),
 		Name:    fs.Provenance().ID,
 	})
 	if err != nil {
diff --git a/pkg/provider/aws/knowledgebase/api_gateway.go b/pkg/provider/aws/knowledgebase/api_gateway.go
index 93520cb9..792ad442 100644
--- a/pkg/provider/aws/knowledgebase/api_gateway.go
+++ b/pkg/provider/aws/knowledgebase/api_gateway.go
@@ -47,7 +47,7 @@ var ApiGatewayKB = knowledgebase.Build(
 				return nil
 			}
 			restApi, ok := data.Source.(*resources.RestApi)
-			refs := core.DedupeAnnotationKeys(append(function.ConstructsRef, restApi.ConstructsRef...))
+			refs := function.ConstructsRef.CloneWith(restApi.ConstructsRef)
 			if !ok {
 				return fmt.Errorf("source of lambda to api integration expansion must be a rest api resource")
 			}
@@ -59,7 +59,7 @@ var ApiGatewayKB = knowledgebase.Build(
 				var err error
 				integration, err = core.CreateResource[*resources.ApiIntegration](dag, resources.ApiIntegrationCreateParams{
 					AppName:    data.AppName,
-					Refs:       refs,
+					Refs:       refs.Clone(),
 					Path:       route.Path,
 					ApiName:    restApi.Name,
 					HttpMethod: strings.ToUpper(string(route.Verb)),
diff --git a/pkg/provider/aws/knowledgebase/iam.go b/pkg/provider/aws/knowledgebase/iam.go
index 77c29eae..0e13bc9b 100644
--- a/pkg/provider/aws/knowledgebase/iam.go
+++ b/pkg/provider/aws/knowledgebase/iam.go
@@ -4,7 +4,6 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/klothoplatform/klotho/pkg/collectionutil"
 	"github.com/klothoplatform/klotho/pkg/core"
 	"github.com/klothoplatform/klotho/pkg/infra/kubernetes"
 	knowledgebase "github.com/klothoplatform/klotho/pkg/knowledge_base"
@@ -57,7 +56,7 @@ var IamKB = knowledgebase.Build(
 				{Resource: table, Property: resources.DYNAMODB_TABLE_STREAM_IAC_VALUE},
 			}
 			doc := resources.CreateAllowPolicyDocument(actions, policyResources)
-			inlinePol := resources.NewIamInlinePolicy(fmt.Sprintf("%s-dynamodb-policy", table.Name), core.DedupeAnnotationKeys(append(role.ConstructsRef, table.ConstructsRef...)), doc)
+			inlinePol := resources.NewIamInlinePolicy(fmt.Sprintf("%s-dynamodb-policy", table.Name), role.ConstructsRef.CloneWith(table.ConstructsRef), doc)
 			role.InlinePolicies = append(role.InlinePolicies, inlinePol)
 			return nil
 		},
@@ -123,18 +122,19 @@ var IamKB = knowledgebase.Build(
 	},
 	knowledgebase.EdgeBuilder[*resources.IamRole, *resources.OpenIdConnectProvider]{
 		Configure: func(role *resources.IamRole, oidc *resources.OpenIdConnectProvider, dag *core.ResourceGraph, data knowledgebase.EdgeData) error {
-			if len(role.ConstructsRef) > 1 {
+			ref, oneRef := role.ConstructsRef.GetSingle()
+			if !oneRef {
 				return fmt.Errorf("iam role %s must only have one construct ref, but has %d, %s", role.Name, len(role.ConstructsRef), role.ConstructsRef)
 			}
-			role.AssumeRolePolicyDoc = resources.GetServiceAccountAssumeRolePolicy(role.ConstructsRef[0].ID, oidc)
-      return nil
+			role.AssumeRolePolicyDoc = resources.GetServiceAccountAssumeRolePolicy(ref.ID, oidc)
+			return nil
 		},
 	},
 	knowledgebase.EdgeBuilder[*resources.IamRole, *resources.S3Bucket]{
 		Configure: func(role *resources.IamRole, bucket *resources.S3Bucket, dag *core.ResourceGraph, data knowledgebase.EdgeData) error {
 			role.InlinePolicies = append(role.InlinePolicies, resources.NewIamInlinePolicy(
 				fmt.Sprintf(`%s-access`, bucket.Name),
-				collectionutil.FlattenUnique(role.ConstructsRef, bucket.ConstructsRef),
+				role.ConstructsRef.CloneWith(bucket.ConstructsRef),
 				resources.CreateAllowPolicyDocument(
 					[]string{"s3:*"},
 					[]core.IaCValue{
diff --git a/pkg/provider/aws/knowledgebase/lambda.go b/pkg/provider/aws/knowledgebase/lambda.go
index 70ea600d..7bc688ba 100644
--- a/pkg/provider/aws/knowledgebase/lambda.go
+++ b/pkg/provider/aws/knowledgebase/lambda.go
@@ -41,7 +41,7 @@ var LambdaKB = knowledgebase.Build(
 				return fmt.Errorf("unable to expand edge [%s -> %s]: lambda function [%s] is not in a VPC",
 					lambda.Id(), instance.Id(), lambda.Id())
 			}
-			inlinePol := resources.NewIamInlinePolicy(fmt.Sprintf("%s-connectionpolicy", instance.Name), core.DedupeAnnotationKeys(append(lambda.ConstructsRef, instance.ConstructsRef...)), instance.GetConnectionPolicyDocument())
+			inlinePol := resources.NewIamInlinePolicy(fmt.Sprintf("%s-connectionpolicy", instance.Name), lambda.ConstructsRef.CloneWith(instance.ConstructsRef), instance.GetConnectionPolicyDocument())
 			lambda.Role.InlinePolicies = append(lambda.Role.InlinePolicies, inlinePol)
 
 			for _, env := range data.EnvironmentVariables {
@@ -73,8 +73,11 @@ var LambdaKB = knowledgebase.Build(
 				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())
+							inlinePol := resources.NewIamInlinePolicy(
+								fmt.Sprintf("%s-connectionpolicy", instance.Name),
+								lambda.ConstructsRef.CloneWith(instance.ConstructsRef),
+								instance.GetConnectionPolicyDocument(),
+							)
 							lambda.Role.InlinePolicies = append(lambda.Role.InlinePolicies, inlinePol)
 							dag.AddDependency(lambda.Role, instance)
 						}
diff --git a/pkg/provider/aws/knowledgebase/rds.go b/pkg/provider/aws/knowledgebase/rds.go
index 87470df0..482cd009 100644
--- a/pkg/provider/aws/knowledgebase/rds.go
+++ b/pkg/provider/aws/knowledgebase/rds.go
@@ -73,7 +73,7 @@ var RdsKB = knowledgebase.Build(
 			}
 			secretVersion, err := core.CreateResource[*resources.SecretVersion](dag, resources.SecretVersionCreateParams{
 				AppName: data.AppName,
-				Refs:    core.DedupeAnnotationKeys(append(targetGroup.KlothoConstructRef(), proxy.KlothoConstructRef()...)),
+				Refs:    targetGroup.KlothoConstructRef().CloneWith(proxy.KlothoConstructRef()),
 				Name:    proxy.Name,
 			})
 			if err != nil {
@@ -121,7 +121,7 @@ var RdsKB = knowledgebase.Build(
 		Expand: func(proxy *resources.RdsProxy, sv *resources.SecretVersion, dag *core.ResourceGraph, data knowledgebase.EdgeData) error {
 			secretVersion, err := core.CreateResource[*resources.SecretVersion](dag, resources.SecretVersionCreateParams{
 				AppName: data.AppName,
-				Refs:    core.DedupeAnnotationKeys(append(proxy.KlothoConstructRef(), sv.KlothoConstructRef()...)),
+				Refs:    proxy.KlothoConstructRef().CloneWith(sv.KlothoConstructRef()),
 				Name:    proxy.Name,
 			})
 			if err != nil {
diff --git a/pkg/provider/aws/kv.go b/pkg/provider/aws/kv.go
index f32edc28..aa4af0db 100644
--- a/pkg/provider/aws/kv.go
+++ b/pkg/provider/aws/kv.go
@@ -8,7 +8,7 @@ import (
 func (a *AWS) expandKv(dag *core.ResourceGraph, kv *core.Kv) error {
 	table, err := core.CreateResource[*resources.DynamodbTable](dag, resources.DynamodbTableCreateParams{
 		AppName: a.Config.AppName,
-		Refs:    []core.AnnotationKey{kv.AnnotationKey},
+		Refs:    core.AnnotationKeySetOf(kv.AnnotationKey),
 		Name:    "kv",
 	})
 	if err != nil {
diff --git a/pkg/provider/aws/orm.go b/pkg/provider/aws/orm.go
index 526fffdd..ace94ab0 100644
--- a/pkg/provider/aws/orm.go
+++ b/pkg/provider/aws/orm.go
@@ -13,7 +13,7 @@ func (a *AWS) expandOrm(dag *core.ResourceGraph, orm *core.Orm) error {
 	case Rds_postgres:
 		instance, err := core.CreateResource[*resources.RdsInstance](dag, resources.RdsInstanceCreateParams{
 			AppName: a.Config.AppName,
-			Refs:    []core.AnnotationKey{orm.AnnotationKey},
+			Refs:    core.AnnotationKeySetOf(orm.AnnotationKey),
 			Name:    orm.ID,
 		})
 		if err != nil {
@@ -29,14 +29,15 @@ func (a *AWS) expandOrm(dag *core.ResourceGraph, orm *core.Orm) error {
 	return nil
 }
 
-func (a *AWS) getRdsConfiguration(result *core.ConstructGraph, dag *core.ResourceGraph, refs []core.AnnotationKey) (resources.RdsInstanceConfigureParams, error) {
-	if len(refs) != 1 {
+func (a *AWS) getRdsConfiguration(result *core.ConstructGraph, dag *core.ResourceGraph, refs core.AnnotationKeySet) (resources.RdsInstanceConfigureParams, error) {
+	ref, oneRef := refs.GetSingle()
+	if !oneRef {
 		return resources.RdsInstanceConfigureParams{}, fmt.Errorf("rds instance must only have one construct reference")
 	}
 	rdsConfig := resources.RdsInstanceConfigureParams{}
-	construct := result.GetConstruct(refs[0].ToId())
+	construct := result.GetConstruct(ref.ToId())
 	if construct == nil {
-		return resources.RdsInstanceConfigureParams{}, fmt.Errorf("construct with id %s does not exist", refs[0].ToId())
+		return resources.RdsInstanceConfigureParams{}, fmt.Errorf("construct with id %s does not exist", ref.ToId())
 	}
 	orm, ok := construct.(*core.Orm)
 	if !ok {
diff --git a/pkg/provider/aws/persist.go b/pkg/provider/aws/persist.go
index 8a5a518e..cbcfdb58 100644
--- a/pkg/provider/aws/persist.go
+++ b/pkg/provider/aws/persist.go
@@ -10,7 +10,7 @@ import (
 func (a *AWS) expandRedisNode(dag *core.ResourceGraph, construct *core.RedisNode) error {
 	redis, err := core.CreateResource[*resources.ElasticacheCluster](dag, resources.ElasticacheClusterCreateParams{
 		AppName: a.Config.AppName,
-		Refs:    []core.AnnotationKey{construct.AnnotationKey},
+		Refs:    core.AnnotationKeySetOf(construct.AnnotationKey),
 		Name:    construct.ID,
 	})
 	if err != nil {
@@ -19,14 +19,15 @@ func (a *AWS) expandRedisNode(dag *core.ResourceGraph, construct *core.RedisNode
 	return a.MapResourceToConstruct(redis, construct)
 }
 
-func (a *AWS) getElasticacheConfiguration(result *core.ConstructGraph, refs []core.AnnotationKey) (resources.ElasticacheClusterConfigureParams, error) {
+func (a *AWS) getElasticacheConfiguration(result *core.ConstructGraph, refs core.AnnotationKeySet) (resources.ElasticacheClusterConfigureParams, error) {
 	clusterConfig := resources.ElasticacheClusterConfigureParams{}
-	if len(refs) != 1 {
+	ref, oneRef := refs.GetSingle()
+	if !oneRef {
 		return clusterConfig, fmt.Errorf("elasticache cluster must only have one construct reference")
 	}
-	construct := result.GetConstruct(refs[0].ToId())
+	construct := result.GetConstruct(ref.ToId())
 	if construct == nil {
-		return clusterConfig, fmt.Errorf("construct with id %s does not exist", refs[0].ToId())
+		return clusterConfig, fmt.Errorf("construct with id %s does not exist", ref)
 	}
 	if _, ok := construct.(*core.RedisNode); !ok {
 		return clusterConfig, fmt.Errorf("elasticache cluster must only have a construct reference to a redis node")
diff --git a/pkg/provider/aws/resources/api_gateway.go b/pkg/provider/aws/resources/api_gateway.go
index 5ac7c824..f7c2e9d1 100644
--- a/pkg/provider/aws/resources/api_gateway.go
+++ b/pkg/provider/aws/resources/api_gateway.go
@@ -29,13 +29,13 @@ var apiResourceSanitizer = aws.ApiResourceSanitizer
 type (
 	RestApi struct {
 		Name             string
-		ConstructsRef    []core.AnnotationKey
+		ConstructsRef    core.AnnotationKeySet
 		BinaryMediaTypes []string
 	}
 
 	ApiResource struct {
 		Name           string
-		ConstructsRef  []core.AnnotationKey
+		ConstructsRef  core.AnnotationKeySet
 		RestApi        *RestApi
 		PathPart       string
 		ParentResource *ApiResource
@@ -43,7 +43,7 @@ type (
 
 	ApiMethod struct {
 		Name              string
-		ConstructsRef     []core.AnnotationKey
+		ConstructsRef     core.AnnotationKeySet
 		RestApi           *RestApi
 		Resource          *ApiResource
 		HttpMethod        string
@@ -52,13 +52,13 @@ type (
 	}
 
 	VpcLink struct {
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		Target        core.Resource
 	}
 
 	ApiIntegration struct {
 		Name                  string
-		ConstructsRef         []core.AnnotationKey
+		ConstructsRef         core.AnnotationKeySet
 		RestApi               *RestApi
 		Resource              *ApiResource
 		Method                *ApiMethod
@@ -73,14 +73,14 @@ type (
 
 	ApiDeployment struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		RestApi       *RestApi
 		Triggers      map[string]string
 	}
 
 	ApiStage struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		StageName     string
 		RestApi       *RestApi
 		Deployment    *ApiDeployment
@@ -89,7 +89,7 @@ type (
 
 type RestApiCreateParams struct {
 	AppName string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 	Name    string
 }
 
@@ -102,10 +102,10 @@ func (api *RestApi) Create(dag *core.ResourceGraph, params RestApiCreateParams)
 	api.Name = name
 	api.ConstructsRef = params.Refs
 
-	existingApi := dag.GetResourceByVertexId(api.Id().String())
+	existingApi := dag.GetResource(api.Id())
 	if existingApi != nil {
 		graphApi := existingApi.(*RestApi)
-		graphApi.ConstructsRef = core.DedupeAnnotationKeys(append(graphApi.ConstructsRef, params.Refs...))
+		graphApi.ConstructsRef.AddAll(params.Refs)
 	} else {
 		dag.AddResource(api)
 	}
@@ -147,7 +147,7 @@ func convertPath(path string, wildcardsToGreedy bool) string {
 
 type ApiResourceCreateParams struct {
 	AppName string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 	Path    string
 	ApiName string
 }
@@ -158,10 +158,10 @@ func (resource *ApiResource) Create(dag *core.ResourceGraph, params ApiResourceC
 	resource.Name = name
 	resource.ConstructsRef = params.Refs
 
-	existingResource := dag.GetResourceByVertexId(resource.Id().String())
+	existingResource := dag.GetResource(resource.Id())
 	if existingResource != nil {
 		graphResource := existingResource.(*ApiResource)
-		graphResource.ConstructsRef = append(graphResource.ConstructsRef, params.Refs...)
+		graphResource.ConstructsRef.AddAll(params.Refs)
 		return nil
 	} else {
 		segments := strings.Split(params.Path, "/")
@@ -191,7 +191,7 @@ func (resource *ApiResource) Create(dag *core.ResourceGraph, params ApiResourceC
 
 type ApiIntegrationCreateParams struct {
 	AppName    string
-	Refs       []core.AnnotationKey
+	Refs       core.AnnotationKeySet
 	Path       string
 	ApiName    string
 	HttpMethod string
@@ -204,10 +204,10 @@ func (integration *ApiIntegration) Create(dag *core.ResourceGraph, params ApiInt
 	integration.Name = name
 	integration.ConstructsRef = params.Refs
 
-	existingResource := dag.GetResourceByVertexId(integration.Id().String())
+	existingResource := dag.GetResource(integration.Id())
 	if existingResource != nil {
 		graphResource := existingResource.(*ApiIntegration)
-		graphResource.ConstructsRef = append(graphResource.ConstructsRef, params.Refs...)
+		graphResource.ConstructsRef.AddAll(params.Refs)
 	} else {
 		subParams := map[string]any{
 			"RestApi": RestApiCreateParams{
@@ -234,7 +234,7 @@ func (integration *ApiIntegration) Create(dag *core.ResourceGraph, params ApiInt
 
 type ApiMethodCreateParams struct {
 	AppName    string
-	Refs       []core.AnnotationKey
+	Refs       core.AnnotationKeySet
 	Path       string
 	ApiName    string
 	HttpMethod string
@@ -251,7 +251,7 @@ func (method *ApiMethod) Create(dag *core.ResourceGraph, params ApiMethodCreateP
 	existingResource := dag.GetResource(method.Id())
 	if existingResource != nil {
 		graphResource := existingResource.(*ApiMethod)
-		graphResource.ConstructsRef = append(graphResource.ConstructsRef, params.Refs...)
+		graphResource.ConstructsRef.AddAll(params.Refs)
 	} else {
 		subParams := map[string]any{
 			"RestApi": RestApiCreateParams{
@@ -290,7 +290,7 @@ func (method *ApiMethod) Configure(params ApiMethodConfigureParams) error {
 
 type ApiDeploymentCreateParams struct {
 	AppName string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 	Name    string
 }
 
@@ -301,10 +301,10 @@ func (deployment *ApiDeployment) Create(dag *core.ResourceGraph, params ApiDeplo
 	deployment.Name = name
 	deployment.ConstructsRef = params.Refs
 
-	existingDeployment := dag.GetResourceByVertexId(deployment.Id().String())
+	existingDeployment := dag.GetResource(deployment.Id())
 	if existingDeployment != nil {
 		graphDeployment := existingDeployment.(*ApiDeployment)
-		graphDeployment.ConstructsRef = append(graphDeployment.ConstructsRef, params.Refs...)
+		graphDeployment.ConstructsRef.AddAll(params.Refs)
 	} else {
 		err := dag.CreateDependencies(deployment, map[string]any{
 			"RestApi": params,
@@ -318,7 +318,7 @@ func (deployment *ApiDeployment) Create(dag *core.ResourceGraph, params ApiDeplo
 
 type ApiStageCreateParams struct {
 	AppName string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 	Name    string
 }
 
@@ -329,10 +329,10 @@ func (stage *ApiStage) Create(dag *core.ResourceGraph, params ApiStageCreatePara
 	stage.Name = name
 	stage.ConstructsRef = params.Refs
 
-	existingResource := dag.GetResourceByVertexId(stage.Id().String())
+	existingResource := dag.GetResource(stage.Id())
 	if existingResource != nil {
 		graphResource := existingResource.(*ApiStage)
-		graphResource.ConstructsRef = append(graphResource.ConstructsRef, params.Refs...)
+		graphResource.ConstructsRef.AddAll(params.Refs)
 		return nil
 	} else {
 		err := dag.CreateDependencies(stage, map[string]any{
@@ -362,13 +362,13 @@ func (stage *ApiStage) Configure(params ApiStageConfigureParams) error {
 func NewRestApi(appName string, gw *core.Gateway) *RestApi {
 	return &RestApi{
 		Name:             restApiSanitizer.Apply(fmt.Sprintf("%s-%s", appName, gw.ID)),
-		ConstructsRef:    []core.AnnotationKey{gw.AnnotationKey},
+		ConstructsRef:    core.AnnotationKeySetOf(gw.AnnotationKey),
 		BinaryMediaTypes: []string{"application/octet-stream", "image/*"},
 	}
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (api *RestApi) KlothoConstructRef() []core.AnnotationKey {
+func (api *RestApi) KlothoConstructRef() core.AnnotationKeySet {
 	return api.ConstructsRef
 }
 
@@ -381,7 +381,7 @@ func (api *RestApi) Id() core.ResourceId {
 	}
 }
 
-func NewApiResource(currSegment string, api *RestApi, refs []core.AnnotationKey, pathPart string, parentResource *ApiResource) *ApiResource {
+func NewApiResource(currSegment string, api *RestApi, refs core.AnnotationKeySet, pathPart string, parentResource *ApiResource) *ApiResource {
 	return &ApiResource{
 		Name:           apiResourceSanitizer.Apply(fmt.Sprintf("%s-%s", api.Name, currSegment)),
 		ConstructsRef:  refs,
@@ -392,7 +392,7 @@ func NewApiResource(currSegment string, api *RestApi, refs []core.AnnotationKey,
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (res *ApiResource) KlothoConstructRef() []core.AnnotationKey {
+func (res *ApiResource) KlothoConstructRef() core.AnnotationKeySet {
 	return res.ConstructsRef
 }
 
@@ -405,7 +405,7 @@ func (res *ApiResource) Id() core.ResourceId {
 	}
 }
 
-func NewApiMethod(resource *ApiResource, api *RestApi, refs []core.AnnotationKey, httpMethod string, requestParams map[string]bool) *ApiMethod {
+func NewApiMethod(resource *ApiResource, api *RestApi, refs core.AnnotationKeySet, httpMethod string, requestParams map[string]bool) *ApiMethod {
 	name := fmt.Sprintf("%s-%s", api.Name, httpMethod)
 	if resource != nil {
 		name = fmt.Sprintf("%s-%s", resource.Name, httpMethod)
@@ -422,7 +422,7 @@ func NewApiMethod(resource *ApiResource, api *RestApi, refs []core.AnnotationKey
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (method *ApiMethod) KlothoConstructRef() []core.AnnotationKey {
+func (method *ApiMethod) KlothoConstructRef() core.AnnotationKeySet {
 	return method.ConstructsRef
 }
 
@@ -435,7 +435,7 @@ func (method *ApiMethod) Id() core.ResourceId {
 	}
 }
 
-func NewVpcLink(resource core.Resource, refs []core.AnnotationKey) *VpcLink {
+func NewVpcLink(resource core.Resource, refs core.AnnotationKeySet) *VpcLink {
 	return &VpcLink{
 		ConstructsRef: refs,
 		Target:        resource,
@@ -443,7 +443,7 @@ func NewVpcLink(resource core.Resource, refs []core.AnnotationKey) *VpcLink {
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (link *VpcLink) KlothoConstructRef() []core.AnnotationKey {
+func (link *VpcLink) KlothoConstructRef() core.AnnotationKeySet {
 	return link.ConstructsRef
 }
 
@@ -464,7 +464,7 @@ func (res *VpcLink) Name() string {
 	return res.Id().Name
 }
 
-func NewApiIntegration(method *ApiMethod, refs []core.AnnotationKey, integrationMethod string, integrationType string, vpcLink *VpcLink, uri core.IaCValue, requestParameters map[string]string) *ApiIntegration {
+func NewApiIntegration(method *ApiMethod, refs core.AnnotationKeySet, integrationMethod string, integrationType string, vpcLink *VpcLink, uri core.IaCValue, requestParameters map[string]string) *ApiIntegration {
 	return &ApiIntegration{
 		Name:                  method.Name,
 		ConstructsRef:         refs,
@@ -480,7 +480,7 @@ func NewApiIntegration(method *ApiMethod, refs []core.AnnotationKey, integration
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (integration *ApiIntegration) KlothoConstructRef() []core.AnnotationKey {
+func (integration *ApiIntegration) KlothoConstructRef() core.AnnotationKeySet {
 	return integration.ConstructsRef
 }
 
@@ -493,7 +493,7 @@ func (integration *ApiIntegration) Id() core.ResourceId {
 	}
 }
 
-func NewApiDeployment(api *RestApi, refs []core.AnnotationKey, triggers map[string]string) *ApiDeployment {
+func NewApiDeployment(api *RestApi, refs core.AnnotationKeySet, triggers map[string]string) *ApiDeployment {
 	return &ApiDeployment{
 		Name:          api.Name,
 		ConstructsRef: refs,
@@ -503,7 +503,7 @@ func NewApiDeployment(api *RestApi, refs []core.AnnotationKey, triggers map[stri
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (deployment *ApiDeployment) KlothoConstructRef() []core.AnnotationKey {
+func (deployment *ApiDeployment) KlothoConstructRef() core.AnnotationKeySet {
 	return deployment.ConstructsRef
 }
 
@@ -516,7 +516,7 @@ func (deployment *ApiDeployment) Id() core.ResourceId {
 	}
 }
 
-func NewApiStage(deployment *ApiDeployment, stageName string, refs []core.AnnotationKey) *ApiStage {
+func NewApiStage(deployment *ApiDeployment, stageName string, refs core.AnnotationKeySet) *ApiStage {
 	return &ApiStage{
 		Name:          fmt.Sprintf("%s-%s", deployment.Name, stageName),
 		ConstructsRef: refs,
@@ -527,7 +527,7 @@ func NewApiStage(deployment *ApiDeployment, stageName string, refs []core.Annota
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (stage *ApiStage) KlothoConstructRef() []core.AnnotationKey {
+func (stage *ApiStage) KlothoConstructRef() core.AnnotationKeySet {
 	return stage.ConstructsRef
 }
 
diff --git a/pkg/provider/aws/resources/cloud_map.go b/pkg/provider/aws/resources/cloud_map.go
index a09d6f48..006b81fa 100644
--- a/pkg/provider/aws/resources/cloud_map.go
+++ b/pkg/provider/aws/resources/cloud_map.go
@@ -14,12 +14,12 @@ const (
 type (
 	PrivateDnsNamespace struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		Vpc           *Vpc
 	}
 )
 
-func NewPrivateDnsNamespace(appName string, refs []core.AnnotationKey, vpc *Vpc) *PrivateDnsNamespace {
+func NewPrivateDnsNamespace(appName string, refs core.AnnotationKeySet, vpc *Vpc) *PrivateDnsNamespace {
 	return &PrivateDnsNamespace{
 		Name:          privateDnsNamespaceSanitizer.Apply(appName),
 		ConstructsRef: refs,
@@ -28,7 +28,7 @@ func NewPrivateDnsNamespace(appName string, refs []core.AnnotationKey, vpc *Vpc)
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (ns *PrivateDnsNamespace) KlothoConstructRef() []core.AnnotationKey {
+func (ns *PrivateDnsNamespace) KlothoConstructRef() core.AnnotationKeySet {
 	return ns.ConstructsRef
 }
 
diff --git a/pkg/provider/aws/resources/cloudfront.go b/pkg/provider/aws/resources/cloudfront.go
index 1a5d94bc..3a0f33dd 100644
--- a/pkg/provider/aws/resources/cloudfront.go
+++ b/pkg/provider/aws/resources/cloudfront.go
@@ -19,7 +19,7 @@ const (
 type (
 	CloudfrontDistribution struct {
 		Name                         string
-		ConstructsRef                []core.AnnotationKey
+		ConstructsRef                core.AnnotationKeySet
 		Origins                      []*CloudfrontOrigin
 		CloudfrontDefaultCertificate bool
 		Enabled                      bool
@@ -77,7 +77,7 @@ type (
 
 	OriginAccessIdentity struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		Comment       string
 	}
 )
@@ -87,7 +87,7 @@ func CreateS3Origin(unit *core.StaticUnit, bucket *S3Bucket, distribution *Cloud
 
 	oai := &OriginAccessIdentity{
 		Name:          fmt.Sprintf("%s-%s", bucket.Name, unit.ID),
-		ConstructsRef: []core.AnnotationKey{unit.AnnotationKey},
+		ConstructsRef: core.AnnotationKeySetOf(unit.AnnotationKey),
 		Comment:       "this is needed to setup s3 polices and make s3 not public.",
 	}
 
@@ -177,7 +177,7 @@ func NewCloudfrontDistribution(appName string, cdnId string) *CloudfrontDistribu
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (distro *CloudfrontDistribution) KlothoConstructRef() []core.AnnotationKey {
+func (distro *CloudfrontDistribution) KlothoConstructRef() core.AnnotationKeySet {
 	return distro.ConstructsRef
 }
 
@@ -191,7 +191,7 @@ func (distro *CloudfrontDistribution) Id() core.ResourceId {
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (oai *OriginAccessIdentity) KlothoConstructRef() []core.AnnotationKey {
+func (oai *OriginAccessIdentity) KlothoConstructRef() core.AnnotationKeySet {
 	return oai.ConstructsRef
 }
 
diff --git a/pkg/provider/aws/resources/cloudwatch.go b/pkg/provider/aws/resources/cloudwatch.go
index 1b3694c9..f5a99443 100644
--- a/pkg/provider/aws/resources/cloudwatch.go
+++ b/pkg/provider/aws/resources/cloudwatch.go
@@ -14,7 +14,7 @@ var logGroupSanitizer = aws.CloudwatchLogGroupSanitizer
 type (
 	LogGroup struct {
 		Name            string
-		ConstructsRef   []core.AnnotationKey
+		ConstructsRef   core.AnnotationKeySet
 		LogGroupName    string
 		RetentionInDays int
 	}
@@ -22,7 +22,7 @@ type (
 
 type CloudwatchLogGroupCreateParams struct {
 	AppName string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 	Name    string
 }
 
@@ -33,7 +33,7 @@ func (logGroup *LogGroup) Create(dag *core.ResourceGraph, params CloudwatchLogGr
 	existingLogGroup := dag.GetResource(logGroup.Id())
 	if existingLogGroup != nil {
 		graphLogGroup := existingLogGroup.(*LogGroup)
-		graphLogGroup.ConstructsRef = core.DedupeAnnotationKeys(append(graphLogGroup.KlothoConstructRef(), params.Refs...))
+		graphLogGroup.ConstructsRef.AddAll(params.Refs)
 	} else {
 		dag.AddResource(logGroup)
 	}
@@ -43,14 +43,14 @@ func (logGroup *LogGroup) Create(dag *core.ResourceGraph, params CloudwatchLogGr
 func NewLogGroup(appName string, logGroupName string, ref core.AnnotationKey, retention int) *LogGroup {
 	return &LogGroup{
 		Name:            logGroupSanitizer.Apply(fmt.Sprintf("%s-%s", appName, logGroupName)),
-		ConstructsRef:   []core.AnnotationKey{ref},
+		ConstructsRef:   core.AnnotationKeySetOf(ref),
 		LogGroupName:    logGroupSanitizer.Apply(logGroupName),
 		RetentionInDays: retention,
 	}
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (lg *LogGroup) KlothoConstructRef() []core.AnnotationKey {
+func (lg *LogGroup) KlothoConstructRef() core.AnnotationKeySet {
 	return lg.ConstructsRef
 }
 
diff --git a/pkg/provider/aws/resources/dynamodb.go b/pkg/provider/aws/resources/dynamodb.go
index 2f9ac71e..d87455f7 100644
--- a/pkg/provider/aws/resources/dynamodb.go
+++ b/pkg/provider/aws/resources/dynamodb.go
@@ -14,7 +14,7 @@ const DYNAMODB_TABLE_TYPE = "dynamodb_table"
 type (
 	DynamodbTable struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		Attributes    []DynamodbTableAttribute
 		BillingMode   string
 		HashKey       string
@@ -80,7 +80,7 @@ func (table *DynamodbTable) AttributeMap() (map[string]DynamodbTableAttribute, e
 
 type DynamodbTableCreateParams struct {
 	AppName string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 	Name    string
 }
 
@@ -88,7 +88,7 @@ func (table *DynamodbTable) Create(dag *core.ResourceGraph, params DynamodbTable
 	table.Name = aws.DynamoDBTableSanitizer.Apply(fmt.Sprintf("%s-%s", params.AppName, params.Name))
 	table.ConstructsRef = params.Refs
 	if existingTable, ok := core.GetResource[*DynamodbTable](dag, table.Id()); ok {
-		existingTable.ConstructsRef = core.DedupeAnnotationKeys(append(existingTable.KlothoConstructRef(), params.Refs...))
+		existingTable.ConstructsRef.AddAll(params.Refs)
 	}
 	dag.AddResource(table)
 	return nil
@@ -110,7 +110,7 @@ func (table *DynamodbTable) Configure(params DynamodbTableConfigureParams) error
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (table *DynamodbTable) KlothoConstructRef() []core.AnnotationKey {
+func (table *DynamodbTable) KlothoConstructRef() core.AnnotationKeySet {
 	return table.ConstructsRef
 }
 
diff --git a/pkg/provider/aws/resources/ecr.go b/pkg/provider/aws/resources/ecr.go
index d15d176e..f6596f3c 100644
--- a/pkg/provider/aws/resources/ecr.go
+++ b/pkg/provider/aws/resources/ecr.go
@@ -16,13 +16,13 @@ const (
 type (
 	EcrRepository struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		ForceDelete   bool
 	}
 
 	EcrImage struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		Repo          *EcrRepository
 		Context       string
 		Dockerfile    string
@@ -32,7 +32,7 @@ type (
 
 type RepoCreateParams struct {
 	AppName string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 }
 
 func (repo *EcrRepository) Create(dag *core.ResourceGraph, params RepoCreateParams) error {
@@ -42,7 +42,7 @@ func (repo *EcrRepository) Create(dag *core.ResourceGraph, params RepoCreatePara
 	existingRepo := dag.GetResource(repo.Id())
 	if existingRepo != nil {
 		graphRepo := existingRepo.(*EcrRepository)
-		graphRepo.ConstructsRef = core.DedupeAnnotationKeys(append(graphRepo.KlothoConstructRef(), params.Refs...))
+		graphRepo.ConstructsRef.AddAll(params.Refs)
 	} else {
 		dag.AddResource(repo)
 	}
@@ -59,7 +59,7 @@ func (repo *EcrRepository) Configure(params EcrRepositoryConfigureParams) error
 
 type ImageCreateParams struct {
 	AppName string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 	Name    string
 }
 
@@ -114,7 +114,7 @@ func GenerateEcrRepoAndImage(appName string, unit *core.ExecutionUnit, dag *core
 		if !ok {
 			return nil, fmt.Errorf("expected resource with id, %s, to be ecr repository", repo.Id())
 		}
-		repo.ConstructsRef = append(repo.ConstructsRef, unit.Provenance())
+		repo.ConstructsRef.Add(unit.Provenance())
 	}
 
 	// Create image and make it dependent on the repository
@@ -128,12 +128,12 @@ func NewEcrRepository(appName string, ref core.AnnotationKey) *EcrRepository {
 	return &EcrRepository{
 		Name:          appName,
 		ForceDelete:   true,
-		ConstructsRef: []core.AnnotationKey{ref},
+		ConstructsRef: core.AnnotationKeySetOf(ref),
 	}
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (repo *EcrRepository) KlothoConstructRef() []core.AnnotationKey {
+func (repo *EcrRepository) KlothoConstructRef() core.AnnotationKeySet {
 	return repo.ConstructsRef
 }
 
@@ -153,7 +153,7 @@ func GenerateRepoId(name string) core.ResourceId {
 func NewEcrImage(unit *core.ExecutionUnit, appName string, repo *EcrRepository) *EcrImage {
 	return &EcrImage{
 		Name:          fmt.Sprintf("%s-%s", appName, unit.ID),
-		ConstructsRef: []core.AnnotationKey{unit.Provenance()},
+		ConstructsRef: core.AnnotationKeySetOf(unit.Provenance()),
 		Repo:          repo,
 		Context:       fmt.Sprintf("./%s", unit.ID),
 		Dockerfile:    fmt.Sprintf("./%s/%s", unit.ID, unit.DockerfilePath),
@@ -162,7 +162,7 @@ func NewEcrImage(unit *core.ExecutionUnit, appName string, repo *EcrRepository)
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (image *EcrImage) KlothoConstructRef() []core.AnnotationKey {
+func (image *EcrImage) KlothoConstructRef() core.AnnotationKeySet {
 	return image.ConstructsRef
 }
 
diff --git a/pkg/provider/aws/resources/eks.go b/pkg/provider/aws/resources/eks.go
index d2b30dcb..5fe780bb 100644
--- a/pkg/provider/aws/resources/eks.go
+++ b/pkg/provider/aws/resources/eks.go
@@ -8,7 +8,6 @@ import (
 	"reflect"
 	"strings"
 
-	"github.com/klothoplatform/klotho/pkg/collectionutil"
 	"github.com/klothoplatform/klotho/pkg/core"
 	"github.com/klothoplatform/klotho/pkg/infra/kubernetes"
 	"github.com/klothoplatform/klotho/pkg/sanitization/aws"
@@ -95,7 +94,7 @@ var eksManifests embed.FS
 type (
 	EksCluster struct {
 		Name           string
-		ConstructsRef  []core.AnnotationKey
+		ConstructsRef  core.AnnotationKeySet
 		ClusterRole    *IamRole
 		Vpc            *Vpc
 		Subnets        []*Subnet
@@ -106,7 +105,7 @@ type (
 
 	EksFargateProfile struct {
 		Name             string
-		ConstructsRef    []core.AnnotationKey
+		ConstructsRef    core.AnnotationKeySet
 		Cluster          *EksCluster
 		PodExecutionRole *IamRole
 		Selectors        []*FargateProfileSelector
@@ -120,7 +119,7 @@ type (
 
 	EksNodeGroup struct {
 		Name           string
-		ConstructsRef  []core.AnnotationKey
+		ConstructsRef  core.AnnotationKeySet
 		Cluster        *EksCluster
 		NodeRole       *IamRole
 		AmiType        string
@@ -136,14 +135,14 @@ type (
 
 	EksAddon struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		AddonName     string
 		ClusterName   core.IaCValue
 	}
 )
 
 type EksClusterCreateParams struct {
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 	AppName string
 	Name    string
 }
@@ -155,7 +154,7 @@ func (cluster *EksCluster) Create(dag *core.ResourceGraph, params EksClusterCrea
 	existingCluster := dag.GetResource(cluster.Id())
 	if existingCluster != nil {
 		graphCluster := existingCluster.(*EksCluster)
-		graphCluster.ConstructsRef = append(graphCluster.ConstructsRef, params.Refs...)
+		graphCluster.ConstructsRef.AddAll(params.Refs)
 	} else {
 		cluster.ConstructsRef = params.Refs
 		cluster.Subnets = make([]*Subnet, 4)
@@ -229,7 +228,7 @@ func (cluster *EksCluster) Configure(params EksClusterConfigureParams) error {
 
 type EksFargateProfileCreateParams struct {
 	ClusterName string
-	Refs        []core.AnnotationKey
+	Refs        core.AnnotationKeySet
 	AppName     string
 	Name        string
 	NetworkType string
@@ -242,7 +241,7 @@ func (profile *EksFargateProfile) Create(dag *core.ResourceGraph, params EksFarg
 	if existingProfile != nil {
 		graphProfile := existingProfile.(*EksFargateProfile)
 
-		graphProfile.ConstructsRef = append(graphProfile.ConstructsRef, params.Refs...)
+		graphProfile.ConstructsRef.AddAll(params.Refs)
 	} else {
 		profile.ConstructsRef = params.Refs
 		profile.Subnets = make([]*Subnet, 2)
@@ -311,7 +310,7 @@ func (profile *EksFargateProfile) Configure(params EksFargateProfileConfigurePar
 type EksNodeGroupCreateParams struct {
 	InstanceType string
 	NetworkType  string
-	Refs         []core.AnnotationKey
+	Refs         core.AnnotationKeySet
 	AppName      string
 	ClusterName  string
 }
@@ -323,7 +322,7 @@ func (nodeGroup *EksNodeGroup) Create(dag *core.ResourceGraph, params EksNodeGro
 
 	existingNodeGroup, found := core.GetResource[*EksNodeGroup](dag, nodeGroup.Id())
 	if found {
-		existingNodeGroup.ConstructsRef = collectionutil.FlattenUnique(existingNodeGroup.ConstructsRef, params.Refs)
+		existingNodeGroup.ConstructsRef.AddAll(params.Refs)
 	} else {
 		nodeGroup.ConstructsRef = params.Refs
 		nodeGroup.InstanceTypes = []string{params.InstanceType}
@@ -446,12 +445,12 @@ func (cluster *EksCluster) InstallNvidiaDevicePlugin(dag *core.ResourceGraph) {
 	for _, ng := range cluster.GetClustersNodeGroups(dag) {
 		dag.AddDependency(manifest, ng)
 		if strings.HasSuffix(strings.ToLower(ng.AmiType), "_gpu") {
-			manifest.ConstructRefs = append(manifest.ConstructRefs, ng.ConstructsRef...)
+			manifest.ConstructRefs.AddAll(ng.ConstructsRef)
 		}
 	}
 }
 
-func (cluster *EksCluster) CreateFargateLogging(references []core.AnnotationKey, dag *core.ResourceGraph) error {
+func (cluster *EksCluster) CreateFargateLogging(references core.AnnotationKeySet, dag *core.ResourceGraph) error {
 	namespaceOutputPath := path.Join(MANIFEST_PATH_PREFIX, AWS_OBSERVABILITY_NS_PATH)
 	content, err := fs.ReadFile(eksManifests, namespaceOutputPath)
 	if err != nil {
@@ -494,7 +493,7 @@ func (cluster *EksCluster) CreateFargateLogging(references []core.AnnotationKey,
 	return nil
 }
 
-func (cluster *EksCluster) InstallFluentBit(references []core.AnnotationKey, dag *core.ResourceGraph) error {
+func (cluster *EksCluster) InstallFluentBit(references core.AnnotationKeySet, dag *core.ResourceGraph) error {
 	namespaceOutputPath := path.Join(MANIFEST_PATH_PREFIX, AMAZON_CLOUDWATCH_NS_PATH)
 	content, err := fs.ReadFile(eksManifests, namespaceOutputPath)
 	if err != nil {
@@ -555,7 +554,7 @@ func (cluster *EksCluster) InstallFluentBit(references []core.AnnotationKey, dag
 func (cluster *EksCluster) InstallCloudMapController(ref core.AnnotationKey, dag *core.ResourceGraph) (*kubernetes.KustomizeDirectory, error) {
 	cloudMapController := &kubernetes.KustomizeDirectory{
 		Name:          fmt.Sprintf("%s-cloudmap-controller", cluster.Name),
-		ConstructRefs: []core.AnnotationKey{ref},
+		ConstructRefs: core.AnnotationKeySetOf(ref),
 		Directory:     "https://github.com/aws/aws-cloud-map-mcs-controller-for-k8s/config/controller_install_release",
 		ClustersProvider: core.IaCValue{
 			Resource: cluster,
@@ -566,7 +565,7 @@ func (cluster *EksCluster) InstallCloudMapController(ref core.AnnotationKey, dag
 	if controller := dag.GetResource(cloudMapController.Id()); controller != nil {
 		if cm, ok := controller.(*kubernetes.KustomizeDirectory); ok {
 			cloudMapController = cm
-			cm.ConstructRefs = append(cm.ConstructRefs, ref)
+			cm.ConstructRefs.Add(ref)
 		} else {
 			return nil, errors.Errorf("Expected resource with id, %s, to be of type HelmChart, but was %s",
 				controller.Id(), reflect.ValueOf(controller).Type().Name())
@@ -579,7 +578,7 @@ func (cluster *EksCluster) InstallCloudMapController(ref core.AnnotationKey, dag
 		}
 		clusterSet := &kubernetes.Manifest{
 			Name:          fmt.Sprintf("%s-%s", cluster.Name, "cluster-set"),
-			ConstructRefs: []core.AnnotationKey{ref},
+			ConstructRefs: core.AnnotationKeySetOf(ref),
 			FilePath:      clusterSetOutputPath,
 			Transformations: map[string]core.IaCValue{
 				`spec["value"]`: {Resource: cluster, Property: NAME_IAC_VALUE},
@@ -602,7 +601,7 @@ func (cluster *EksCluster) InstallCloudMapController(ref core.AnnotationKey, dag
 	return cloudMapController, nil
 }
 
-func (cluster *EksCluster) InstallAlbController(references []core.AnnotationKey, dag *core.ResourceGraph) (*kubernetes.HelmChart, error) {
+func (cluster *EksCluster) InstallAlbController(references core.AnnotationKeySet, dag *core.ResourceGraph) (*kubernetes.HelmChart, error) {
 	serviceAccountName := "aws-load-balancer-controller"
 	saPath := "aws-load-balancer-controller-service-account.yaml"
 	outputPath := path.Join(MANIFEST_PATH_PREFIX, saPath)
@@ -615,7 +614,12 @@ func (cluster *EksCluster) InstallAlbController(references []core.AnnotationKey,
 		return nil, err
 	}
 	role := NewIamRole(cluster.Name, "alb-controller", references, assumeRolePolicyDoc)
-	policy := createAlbControllerPolicy(cluster.Name, references[0])
+	var aRef core.AnnotationKey
+	for ref := range references {
+		aRef = ref
+		break
+	}
+	policy := createAlbControllerPolicy(cluster.Name, aRef)
 	role.AddManagedPolicy(core.IaCValue{Resource: policy, Property: ARN_IAC_VALUE})
 
 	if err != nil {
@@ -673,7 +677,7 @@ func (cluster *EksCluster) InstallAlbController(references []core.AnnotationKey,
 	return albChart, nil
 }
 
-func (cluster *EksCluster) installVpcCniAddon(references []core.AnnotationKey, dag *core.ResourceGraph) {
+func (cluster *EksCluster) installVpcCniAddon(references core.AnnotationKeySet, dag *core.ResourceGraph) {
 	addonName := "vpc-cni"
 	addon := &EksAddon{
 		Name:          fmt.Sprintf("%s-addon-%s", cluster.Name, addonName),
@@ -796,7 +800,7 @@ func GetServiceAccountAssumeRolePolicy(serviceAccountName string, oidc *OpenIdCo
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (cluster *EksCluster) KlothoConstructRef() []core.AnnotationKey {
+func (cluster *EksCluster) KlothoConstructRef() core.AnnotationKeySet {
 	return cluster.ConstructsRef
 }
 
@@ -810,7 +814,7 @@ func (cluster *EksCluster) Id() core.ResourceId {
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (addon *EksAddon) KlothoConstructRef() []core.AnnotationKey {
+func (addon *EksAddon) KlothoConstructRef() core.AnnotationKeySet {
 	return addon.ConstructsRef
 }
 
@@ -824,7 +828,7 @@ func (addon *EksAddon) Id() core.ResourceId {
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (profile *EksFargateProfile) KlothoConstructRef() []core.AnnotationKey {
+func (profile *EksFargateProfile) KlothoConstructRef() core.AnnotationKeySet {
 	return profile.ConstructsRef
 }
 
@@ -838,7 +842,7 @@ func (profile *EksFargateProfile) Id() core.ResourceId {
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (group *EksNodeGroup) KlothoConstructRef() []core.AnnotationKey {
+func (group *EksNodeGroup) KlothoConstructRef() core.AnnotationKeySet {
 	return group.ConstructsRef
 }
 
diff --git a/pkg/provider/aws/resources/elasticache.go b/pkg/provider/aws/resources/elasticache.go
index 2602d05b..3da868a8 100644
--- a/pkg/provider/aws/resources/elasticache.go
+++ b/pkg/provider/aws/resources/elasticache.go
@@ -14,7 +14,7 @@ type (
 		CloudwatchGroup *LogGroup
 		SubnetGroup     *ElasticacheSubnetgroup
 		SecurityGroups  []*SecurityGroup
-		ConstructsRef   []core.AnnotationKey
+		ConstructsRef   core.AnnotationKeySet
 		NodeType        string
 		NumCacheNodes   int
 	}
@@ -22,7 +22,7 @@ type (
 	ElasticacheSubnetgroup struct {
 		Name          string
 		Subnets       []*Subnet
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 	}
 )
 
@@ -32,7 +32,7 @@ const (
 )
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (ec *ElasticacheCluster) KlothoConstructRef() []core.AnnotationKey {
+func (ec *ElasticacheCluster) KlothoConstructRef() core.AnnotationKeySet {
 	return ec.ConstructsRef
 }
 
@@ -46,7 +46,7 @@ func (ec *ElasticacheCluster) Id() core.ResourceId {
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (ecsn *ElasticacheSubnetgroup) KlothoConstructRef() []core.AnnotationKey {
+func (ecsn *ElasticacheSubnetgroup) KlothoConstructRef() core.AnnotationKeySet {
 	return ecsn.ConstructsRef
 }
 
@@ -61,7 +61,7 @@ func (ecsn *ElasticacheSubnetgroup) Id() core.ResourceId {
 
 type ElasticacheClusterCreateParams struct {
 	AppName string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 	Name    string
 }
 
@@ -71,7 +71,7 @@ func (ec *ElasticacheCluster) Create(dag *core.ResourceGraph, params Elasticache
 	ec.SecurityGroups = make([]*SecurityGroup, 1)
 
 	if existingCluster, ok := core.GetResource[*ElasticacheCluster](dag, ec.Id()); ok {
-		existingCluster.ConstructsRef = core.DedupeAnnotationKeys(append(existingCluster.KlothoConstructRef(), params.Refs...))
+		existingCluster.ConstructsRef.AddAll(params.Refs)
 	}
 
 	subParams := map[string]any{
@@ -105,7 +105,7 @@ func (ec *ElasticacheCluster) Configure(params ElasticacheClusterConfigureParams
 }
 
 type ElasticacheSubnetgroupCreateParams struct {
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 	AppName string
 	Name    string
 }
@@ -115,7 +115,7 @@ func (ecsn *ElasticacheSubnetgroup) Create(dag *core.ResourceGraph, params Elast
 	ecsn.ConstructsRef = params.Refs
 	ecsn.Subnets = make([]*Subnet, 2)
 	if existingSubnetGroup, ok := core.GetResource[*ElasticacheSubnetgroup](dag, ecsn.Id()); ok {
-		existingSubnetGroup.ConstructsRef = core.DedupeAnnotationKeys(append(existingSubnetGroup.KlothoConstructRef(), params.Refs...))
+		existingSubnetGroup.ConstructsRef.AddAll(params.Refs)
 	}
 
 	subParams := map[string]any{
diff --git a/pkg/provider/aws/resources/iam.go b/pkg/provider/aws/resources/iam.go
index 69c00f32..b538b9b4 100644
--- a/pkg/provider/aws/resources/iam.go
+++ b/pkg/provider/aws/resources/iam.go
@@ -89,7 +89,7 @@ var EKS_ASSUME_ROLE_POLICY = &PolicyDocument{
 type (
 	IamRole struct {
 		Name                string
-		ConstructsRef       []core.AnnotationKey
+		ConstructsRef       core.AnnotationKeySet
 		AssumeRolePolicyDoc *PolicyDocument
 		ManagedPolicies     []core.IaCValue
 		AwsManagedPolicies  []string
@@ -98,13 +98,13 @@ type (
 
 	IamPolicy struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		Policy        *PolicyDocument
 	}
 
 	IamInlinePolicy struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		Policy        *PolicyDocument
 	}
 
@@ -140,7 +140,7 @@ type (
 
 	OpenIdConnectProvider struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		ClientIdLists []string
 		Cluster       *EksCluster
 		Region        *Region
@@ -150,7 +150,7 @@ type (
 type RoleCreateParams struct {
 	AppName string
 	Name    string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 }
 
 func (role *IamRole) Create(dag *core.ResourceGraph, params RoleCreateParams) error {
@@ -169,7 +169,7 @@ func (role *IamRole) Create(dag *core.ResourceGraph, params RoleCreateParams) er
 type IamPolicyCreateParams struct {
 	AppName string
 	Name    string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 }
 
 func (policy *IamPolicy) Create(dag *core.ResourceGraph, params IamPolicyCreateParams) error {
@@ -186,16 +186,16 @@ func (policy *IamPolicy) Create(dag *core.ResourceGraph, params IamPolicyCreateP
 type OidcCreateParams struct {
 	AppName     string
 	ClusterName string
-	Refs        []core.AnnotationKey
+	Refs        core.AnnotationKeySet
 }
 
 func (oidc *OpenIdConnectProvider) Create(dag *core.ResourceGraph, params OidcCreateParams) error {
 	oidc.Name = fmt.Sprintf("%s-%s", params.AppName, params.ClusterName)
 
-	existingOidc := dag.GetResourceByVertexId(oidc.Id().String())
+	existingOidc := dag.GetResource(oidc.Id())
 	if existingOidc != nil {
 		graphOidc := existingOidc.(*OpenIdConnectProvider)
-		graphOidc.ConstructsRef = append(graphOidc.ConstructsRef, params.Refs...)
+		graphOidc.ConstructsRef.AddAll(params.Refs)
 	} else {
 		oidc.ConstructsRef = params.Refs
 		oidc.Region = NewRegion()
@@ -291,7 +291,7 @@ func CreateAllowPolicyDocument(actions []string, resources []core.IaCValue) *Pol
 	}
 }
 
-func NewIamRole(appName string, roleName string, ref []core.AnnotationKey, assumeRolePolicy *PolicyDocument) *IamRole {
+func NewIamRole(appName string, roleName string, ref core.AnnotationKeySet, assumeRolePolicy *PolicyDocument) *IamRole {
 	return &IamRole{
 		Name:                roleSanitizer.Apply(fmt.Sprintf("%s-%s", appName, roleName)),
 		ConstructsRef:       ref,
@@ -300,7 +300,7 @@ func NewIamRole(appName string, roleName string, ref []core.AnnotationKey, assum
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (role *IamRole) KlothoConstructRef() []core.AnnotationKey {
+func (role *IamRole) KlothoConstructRef() core.AnnotationKeySet {
 	return role.ConstructsRef
 }
 
@@ -341,12 +341,12 @@ func (role *IamRole) AddManagedPolicy(policy core.IaCValue) {
 func NewIamPolicy(appName string, policyName string, ref core.AnnotationKey, policy *PolicyDocument) *IamPolicy {
 	return &IamPolicy{
 		Name:          policySanitizer.Apply(fmt.Sprintf("%s-%s", appName, policyName)),
-		ConstructsRef: []core.AnnotationKey{ref},
+		ConstructsRef: core.AnnotationKeySetOf(ref),
 		Policy:        policy,
 	}
 }
 
-func NewIamInlinePolicy(policyName string, refs []core.AnnotationKey, policy *PolicyDocument) *IamInlinePolicy {
+func NewIamInlinePolicy(policyName string, refs core.AnnotationKeySet, policy *PolicyDocument) *IamInlinePolicy {
 	return &IamInlinePolicy{
 		Name:          policySanitizer.Apply(policyName),
 		ConstructsRef: refs,
@@ -355,7 +355,7 @@ func NewIamInlinePolicy(policyName string, refs []core.AnnotationKey, policy *Po
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (policy *IamPolicy) KlothoConstructRef() []core.AnnotationKey {
+func (policy *IamPolicy) KlothoConstructRef() core.AnnotationKeySet {
 	return policy.ConstructsRef
 }
 
@@ -369,7 +369,7 @@ func (policy *IamPolicy) Id() core.ResourceId {
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (oidc *OpenIdConnectProvider) KlothoConstructRef() []core.AnnotationKey {
+func (oidc *OpenIdConnectProvider) KlothoConstructRef() core.AnnotationKeySet {
 	return oidc.ConstructsRef
 }
 
diff --git a/pkg/provider/aws/resources/lambda.go b/pkg/provider/aws/resources/lambda.go
index 29769f15..8aea9996 100644
--- a/pkg/provider/aws/resources/lambda.go
+++ b/pkg/provider/aws/resources/lambda.go
@@ -19,7 +19,7 @@ var LambdaPermissionSanitizer = aws.LambdaPermissionSanitizer
 type (
 	LambdaFunction struct {
 		Name                 string
-		ConstructsRef        []core.AnnotationKey
+		ConstructsRef        core.AnnotationKeySet
 		Role                 *IamRole
 		Image                *EcrImage
 		EnvironmentVariables EnvironmentVariables
@@ -31,7 +31,7 @@ type (
 
 	LambdaPermission struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		Function      *LambdaFunction
 		Principal     string
 		Source        core.IaCValue
@@ -41,7 +41,7 @@ type (
 
 type LambdaCreateParams struct {
 	AppName string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 	Name    string
 }
 
@@ -51,7 +51,7 @@ func (lambda *LambdaFunction) Create(dag *core.ResourceGraph, params LambdaCreat
 	lambda.Name = name
 	lambda.ConstructsRef = params.Refs
 
-	existingLambda := dag.GetResourceByVertexId(lambda.Id().String())
+	existingLambda := dag.GetResource(lambda.Id())
 	if existingLambda != nil {
 		return fmt.Errorf("lambda with name %s already exists", name)
 	}
@@ -105,7 +105,7 @@ func (lambda *LambdaFunction) Configure(params LambdaFunctionConfigureParams) er
 
 type LambdaPermissionCreateParams struct {
 	AppName string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 	Name    string
 }
 
@@ -120,7 +120,7 @@ func (permission *LambdaPermission) Create(dag *core.ResourceGraph, params Lambd
 	existingLambdaPermission := dag.GetResource(permission.Id())
 	if existingLambdaPermission != nil {
 		graphLambdaPermission := existingLambdaPermission.(*LambdaPermission)
-		graphLambdaPermission.ConstructsRef = core.DedupeAnnotationKeys(append(graphLambdaPermission.ConstructsRef, params.Refs...))
+		graphLambdaPermission.ConstructsRef.AddAll(params.Refs)
 		return nil
 	}
 	dag.AddResource(permission)
@@ -131,7 +131,7 @@ func NewLambdaFunction(unit *core.ExecutionUnit, cfg *config.Application, role *
 	params := config.ConvertFromInfraParams[config.ServerlessTypeParams](cfg.GetExecutionUnit(unit.ID).InfraParams)
 	return &LambdaFunction{
 		Name:          lambdaFunctionSanitizer.Apply(fmt.Sprintf("%s-%s", cfg.AppName, unit.ID)),
-		ConstructsRef: []core.AnnotationKey{unit.Provenance()},
+		ConstructsRef: core.AnnotationKeySetOf(unit.Provenance()),
 		Role:          role,
 		Image:         image,
 		MemorySize:    params.Memory,
@@ -140,7 +140,7 @@ func NewLambdaFunction(unit *core.ExecutionUnit, cfg *config.Application, role *
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (lambda *LambdaFunction) KlothoConstructRef() []core.AnnotationKey {
+func (lambda *LambdaFunction) KlothoConstructRef() core.AnnotationKeySet {
 	return lambda.ConstructsRef
 }
 
@@ -153,7 +153,7 @@ func (lambda *LambdaFunction) Id() core.ResourceId {
 	}
 }
 
-func NewLambdaPermission(function *LambdaFunction, source core.IaCValue, principal string, action string, ref []core.AnnotationKey) *LambdaPermission {
+func NewLambdaPermission(function *LambdaFunction, source core.IaCValue, principal string, action string, ref core.AnnotationKeySet) *LambdaPermission {
 	return &LambdaPermission{
 		Name:          LambdaPermissionSanitizer.Apply(fmt.Sprintf("%s-%s", function.Name, source.Resource.Id())),
 		ConstructsRef: ref,
@@ -165,7 +165,7 @@ func NewLambdaPermission(function *LambdaFunction, source core.IaCValue, princip
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (permission *LambdaPermission) KlothoConstructRef() []core.AnnotationKey {
+func (permission *LambdaPermission) KlothoConstructRef() core.AnnotationKeySet {
 	return permission.ConstructsRef
 }
 
diff --git a/pkg/provider/aws/resources/lb.go b/pkg/provider/aws/resources/lb.go
index c4d40b5c..fe837f3c 100644
--- a/pkg/provider/aws/resources/lb.go
+++ b/pkg/provider/aws/resources/lb.go
@@ -22,7 +22,7 @@ const (
 type (
 	LoadBalancer struct {
 		Name                   string
-		ConstructsRef          []core.AnnotationKey
+		ConstructsRef          core.AnnotationKeySet
 		IpAddressType          string
 		LoadBalancerAttributes map[string]string
 		Scheme                 string
@@ -34,7 +34,7 @@ type (
 
 	TargetGroup struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		Port          int
 		Protocol      string
 		Vpc           *Vpc
@@ -44,7 +44,7 @@ type (
 
 	Listener struct {
 		Name           string
-		ConstructsRef  []core.AnnotationKey
+		ConstructsRef  core.AnnotationKeySet
 		Port           int
 		Protocol       string
 		LoadBalancer   *LoadBalancer
@@ -56,7 +56,7 @@ type (
 	}
 )
 
-func NewLoadBalancer(appName string, lbName string, refs []core.AnnotationKey, scheme string, lbType string, subnets []*Subnet, securityGroups []*SecurityGroup) *LoadBalancer {
+func NewLoadBalancer(appName string, lbName string, refs core.AnnotationKeySet, scheme string, lbType string, subnets []*Subnet, securityGroups []*SecurityGroup) *LoadBalancer {
 	return &LoadBalancer{
 		Name:           loadBalancerSanitizer.Apply(fmt.Sprintf("%s-%s", appName, lbName)),
 		ConstructsRef:  refs,
@@ -68,7 +68,7 @@ func NewLoadBalancer(appName string, lbName string, refs []core.AnnotationKey, s
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (lb *LoadBalancer) KlothoConstructRef() []core.AnnotationKey {
+func (lb *LoadBalancer) KlothoConstructRef() core.AnnotationKeySet {
 	return lb.ConstructsRef
 }
 
@@ -81,7 +81,7 @@ func (lb *LoadBalancer) Id() core.ResourceId {
 	}
 }
 
-func NewTargetGroup(appName string, tgName string, refs []core.AnnotationKey, port int, protocol string, vpc *Vpc, targetType string) *TargetGroup {
+func NewTargetGroup(appName string, tgName string, refs core.AnnotationKeySet, port int, protocol string, vpc *Vpc, targetType string) *TargetGroup {
 	return &TargetGroup{
 		Name:          targetGroupSanitizer.Apply(fmt.Sprintf("%s-%s", appName, tgName)),
 		ConstructsRef: refs,
@@ -93,7 +93,7 @@ func NewTargetGroup(appName string, tgName string, refs []core.AnnotationKey, po
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (tg *TargetGroup) KlothoConstructRef() []core.AnnotationKey {
+func (tg *TargetGroup) KlothoConstructRef() core.AnnotationKeySet {
 	return tg.ConstructsRef
 }
 
@@ -106,7 +106,7 @@ func (tg *TargetGroup) Id() core.ResourceId {
 	}
 }
 
-func NewListener(name string, lb *LoadBalancer, ref []core.AnnotationKey, port int, protocol string, defaultActions []*LBAction) *Listener {
+func NewListener(name string, lb *LoadBalancer, ref core.AnnotationKeySet, port int, protocol string, defaultActions []*LBAction) *Listener {
 	return &Listener{
 		Name:           targetGroupSanitizer.Apply(fmt.Sprintf("%s-%s", lb.Name, name)),
 		ConstructsRef:  ref,
@@ -118,7 +118,7 @@ func NewListener(name string, lb *LoadBalancer, ref []core.AnnotationKey, port i
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (tg *Listener) KlothoConstructRef() []core.AnnotationKey {
+func (tg *Listener) KlothoConstructRef() core.AnnotationKeySet {
 	return tg.ConstructsRef
 }
 
diff --git a/pkg/provider/aws/resources/rds.go b/pkg/provider/aws/resources/rds.go
index 442a8f37..f0fd01a6 100644
--- a/pkg/provider/aws/resources/rds.go
+++ b/pkg/provider/aws/resources/rds.go
@@ -41,7 +41,7 @@ type (
 	// RdsInstance represents an AWS RDS db instance
 	RdsInstance struct {
 		Name                             string
-		ConstructsRef                    []core.AnnotationKey
+		ConstructsRef                    core.AnnotationKeySet
 		SubnetGroup                      *RdsSubnetGroup
 		SecurityGroups                   []*SecurityGroup
 		DatabaseName                     string
@@ -60,7 +60,7 @@ type (
 	// RdsSubnetGroup represents an AWS RDS subnet group
 	RdsSubnetGroup struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		Subnets       []*Subnet
 		Tags          map[string]string
 	}
@@ -68,7 +68,7 @@ type (
 	// RdsProxy represents an AWS RDS proxy instance
 	RdsProxy struct {
 		Name              string
-		ConstructsRef     []core.AnnotationKey
+		ConstructsRef     core.AnnotationKeySet
 		DebugLogging      bool
 		EngineFamily      string
 		IdleClientTimeout int
@@ -89,7 +89,7 @@ type (
 	// RdsProxyTargetGroup represents an AWS RDS proxy target group
 	RdsProxyTargetGroup struct {
 		Name                            string
-		ConstructsRef                   []core.AnnotationKey
+		ConstructsRef                   core.AnnotationKeySet
 		RdsInstance                     *RdsInstance
 		RdsProxy                        *RdsProxy
 		TargetGroupName                 string
@@ -108,7 +108,7 @@ type (
 
 type RdsInstanceCreateParams struct {
 	AppName string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 	Name    string
 }
 
@@ -165,7 +165,7 @@ func (instance *RdsInstance) Configure(params RdsInstanceConfigureParams) error
 type RdsSubnetGroupCreateParams struct {
 	AppName string
 	Name    string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 }
 
 func (subnetGroup *RdsSubnetGroup) Create(dag *core.ResourceGraph, params RdsSubnetGroupCreateParams) error {
@@ -175,7 +175,7 @@ func (subnetGroup *RdsSubnetGroup) Create(dag *core.ResourceGraph, params RdsSub
 	existingSubnetGroup := dag.GetResource(subnetGroup.Id())
 	if existingSubnetGroup != nil {
 		graphSubnetGroup := existingSubnetGroup.(*RdsSubnetGroup)
-		graphSubnetGroup.ConstructsRef = core.DedupeAnnotationKeys(append(graphSubnetGroup.KlothoConstructRef(), params.Refs...))
+		graphSubnetGroup.ConstructsRef.AddAll(params.Refs)
 		return nil
 	} else {
 		subnetGroup.Subnets = make([]*Subnet, 2)
@@ -205,7 +205,7 @@ func (subnetGroup *RdsSubnetGroup) Create(dag *core.ResourceGraph, params RdsSub
 type RdsProxyCreateParams struct {
 	AppName string
 	Name    string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 }
 
 func (proxy *RdsProxy) Create(dag *core.ResourceGraph, params RdsProxyCreateParams) error {
@@ -215,7 +215,7 @@ func (proxy *RdsProxy) Create(dag *core.ResourceGraph, params RdsProxyCreatePara
 	existingProxy := dag.GetResource(proxy.Id())
 	if existingProxy != nil {
 		graphProxy := existingProxy.(*RdsProxy)
-		graphProxy.ConstructsRef = core.DedupeAnnotationKeys(append(graphProxy.KlothoConstructRef(), params.Refs...))
+		graphProxy.ConstructsRef.AddAll(params.Refs)
 		return nil
 	} else {
 		proxy.Subnets = make([]*Subnet, 2)
@@ -272,7 +272,7 @@ func (proxy *RdsProxy) Configure(params RdsProxyConfigureParams) error {
 type RdsProxyTargetGroupCreateParams struct {
 	AppName string
 	Name    string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 }
 
 func (tg *RdsProxyTargetGroup) Create(dag *core.ResourceGraph, params RdsProxyTargetGroupCreateParams) error {
@@ -282,7 +282,7 @@ func (tg *RdsProxyTargetGroup) Create(dag *core.ResourceGraph, params RdsProxyTa
 	existingTG := dag.GetResource(tg.Id())
 	if existingTG != nil {
 		graphTG := existingTG.(*RdsProxyTargetGroup)
-		graphTG.ConstructsRef = core.DedupeAnnotationKeys(append(graphTG.KlothoConstructRef(), params.Refs...))
+		graphTG.ConstructsRef.AddAll(params.Refs)
 		return nil
 	} else {
 		dag.AddResource(tg)
@@ -345,7 +345,7 @@ func generatePassword() string {
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (rds *RdsInstance) KlothoConstructRef() []core.AnnotationKey {
+func (rds *RdsInstance) KlothoConstructRef() core.AnnotationKeySet {
 	return rds.ConstructsRef
 }
 
@@ -362,7 +362,7 @@ func (rds *RdsInstance) GetOutputFiles() []core.File {
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (rds *RdsSubnetGroup) KlothoConstructRef() []core.AnnotationKey {
+func (rds *RdsSubnetGroup) KlothoConstructRef() core.AnnotationKeySet {
 	return rds.ConstructsRef
 }
 
@@ -376,7 +376,7 @@ func (rds *RdsSubnetGroup) Id() core.ResourceId {
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (rds *RdsProxy) KlothoConstructRef() []core.AnnotationKey {
+func (rds *RdsProxy) KlothoConstructRef() core.AnnotationKeySet {
 	return rds.ConstructsRef
 }
 
@@ -390,7 +390,7 @@ func (rds *RdsProxy) Id() core.ResourceId {
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (rds *RdsProxyTargetGroup) KlothoConstructRef() []core.AnnotationKey {
+func (rds *RdsProxyTargetGroup) KlothoConstructRef() core.AnnotationKeySet {
 	return rds.ConstructsRef
 }
 
diff --git a/pkg/provider/aws/resources/region.go b/pkg/provider/aws/resources/region.go
index 3c0c8be7..cf44d3c7 100644
--- a/pkg/provider/aws/resources/region.go
+++ b/pkg/provider/aws/resources/region.go
@@ -7,17 +7,17 @@ import (
 type (
 	Region struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 	}
 
 	AvailabilityZones struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 	}
 
 	AccountId struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 	}
 )
 
@@ -34,12 +34,12 @@ const (
 func NewRegion() *Region {
 	return &Region{
 		Name:          REGION_NAME,
-		ConstructsRef: []core.AnnotationKey{},
+		ConstructsRef: core.AnnotationKeySetOf(),
 	}
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (region *Region) KlothoConstructRef() []core.AnnotationKey {
+func (region *Region) KlothoConstructRef() core.AnnotationKeySet {
 	return region.ConstructsRef
 }
 
@@ -55,12 +55,12 @@ func (region *Region) Id() core.ResourceId {
 func NewAvailabilityZones() *AvailabilityZones {
 	return &AvailabilityZones{
 		Name:          AVAILABILITY_ZONES_NAME,
-		ConstructsRef: []core.AnnotationKey{},
+		ConstructsRef: core.AnnotationKeySetOf(),
 	}
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (azs *AvailabilityZones) KlothoConstructRef() []core.AnnotationKey {
+func (azs *AvailabilityZones) KlothoConstructRef() core.AnnotationKeySet {
 	return azs.ConstructsRef
 }
 
@@ -76,12 +76,12 @@ func (azs *AvailabilityZones) Id() core.ResourceId {
 func NewAccountId() *AccountId {
 	return &AccountId{
 		Name:          ACCOUNT_ID_NAME,
-		ConstructsRef: []core.AnnotationKey{},
+		ConstructsRef: core.AnnotationKeySetOf(),
 	}
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (id *AccountId) KlothoConstructRef() []core.AnnotationKey {
+func (id *AccountId) KlothoConstructRef() core.AnnotationKeySet {
 	return id.ConstructsRef
 }
 
diff --git a/pkg/provider/aws/resources/s3.go b/pkg/provider/aws/resources/s3.go
index 5d1721d2..686e77fa 100644
--- a/pkg/provider/aws/resources/s3.go
+++ b/pkg/provider/aws/resources/s3.go
@@ -3,7 +3,6 @@ package resources
 import (
 	"fmt"
 
-	"github.com/klothoplatform/klotho/pkg/collectionutil"
 	"github.com/klothoplatform/klotho/pkg/core"
 	"github.com/klothoplatform/klotho/pkg/sanitization/aws"
 	"github.com/pkg/errors"
@@ -24,14 +23,14 @@ const (
 type (
 	S3Bucket struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		ForceDestroy  bool
 		IndexDocument string
 	}
 
 	S3Object struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		Bucket        *S3Bucket
 		Key           string
 		FilePath      string
@@ -39,7 +38,7 @@ type (
 
 	S3BucketPolicy struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		Bucket        *S3Bucket
 		Policy        *PolicyDocument
 	}
@@ -48,13 +47,13 @@ type (
 func NewS3Bucket(fs core.Construct, appName string) *S3Bucket {
 	return &S3Bucket{
 		Name:          bucketSanitizer.Apply(fmt.Sprintf("%s-%s", appName, fs.Provenance().ID)),
-		ConstructsRef: []core.AnnotationKey{fs.Provenance()},
+		ConstructsRef: core.AnnotationKeySetOf(fs.Provenance()),
 		ForceDestroy:  true,
 	}
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (bucket *S3Bucket) KlothoConstructRef() []core.AnnotationKey {
+func (bucket *S3Bucket) KlothoConstructRef() core.AnnotationKeySet {
 	return bucket.ConstructsRef
 }
 
@@ -69,7 +68,7 @@ func (bucket *S3Bucket) Id() core.ResourceId {
 
 type S3BucketCreateParams struct {
 	AppName string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 	Name    string
 }
 
@@ -83,7 +82,7 @@ func (bucket *S3Bucket) Create(dag *core.ResourceGraph, params S3BucketCreatePar
 		}
 		// Multiple resources may create the same bucket (today, this specifically happens with our payload bucket). If
 		// that happens, just append the refs and exit early; the rest would have been idempotent.
-		existingS3.ConstructsRef = collectionutil.FlattenUnique(existingS3.ConstructsRef, params.Refs)
+		existingS3.ConstructsRef.AddAll(params.Refs)
 		return nil
 	}
 
@@ -114,7 +113,7 @@ func NewS3Object(bucket *S3Bucket, objectName string, key string, path string) *
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (object *S3Object) KlothoConstructRef() []core.AnnotationKey {
+func (object *S3Object) KlothoConstructRef() core.AnnotationKeySet {
 	return object.ConstructsRef
 }
 
@@ -137,7 +136,7 @@ func NewBucketPolicy(policyName string, bucket *S3Bucket, policy *PolicyDocument
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (policy *S3BucketPolicy) KlothoConstructRef() []core.AnnotationKey {
+func (policy *S3BucketPolicy) KlothoConstructRef() core.AnnotationKeySet {
 	return policy.ConstructsRef
 }
 
diff --git a/pkg/provider/aws/resources/secret.go b/pkg/provider/aws/resources/secret.go
index ffbac366..e262749b 100644
--- a/pkg/provider/aws/resources/secret.go
+++ b/pkg/provider/aws/resources/secret.go
@@ -11,14 +11,14 @@ type (
 	Secret struct {
 		Name          string
 		SecretName    string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 	}
 
 	SecretVersion struct {
 		SecretName    string
 		Secret        *Secret
 		Path          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		Name          string
 		Type          string
 	}
@@ -29,7 +29,7 @@ const SECRET_VERSION_TYPE = "secret_version"
 
 type SecretCreateParams struct {
 	AppName string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 	Name    string
 }
 
@@ -47,7 +47,7 @@ func (secret *Secret) Create(dag *core.ResourceGraph, params SecretCreateParams)
 
 type SecretVersionCreateParams struct {
 	AppName string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 	Name    string
 }
 
@@ -91,7 +91,7 @@ func NewSecret(annot core.AnnotationKey, secretName string, appName string) *Sec
 	return &Secret{
 		Name:          plainName,
 		SecretName:    aws.SecretSanitizer.Apply(plainName),
-		ConstructsRef: []core.AnnotationKey{annot},
+		ConstructsRef: core.AnnotationKeySetOf(annot),
 	}
 }
 
@@ -105,7 +105,7 @@ func NewSecretVersion(secret *Secret, filePath string) *SecretVersion {
 	}
 }
 
-func (s *Secret) KlothoConstructRef() []core.AnnotationKey {
+func (s *Secret) KlothoConstructRef() core.AnnotationKeySet {
 	return s.ConstructsRef
 }
 
@@ -117,7 +117,7 @@ func (s *Secret) Id() core.ResourceId {
 	}
 }
 
-func (sv *SecretVersion) KlothoConstructRef() []core.AnnotationKey {
+func (sv *SecretVersion) KlothoConstructRef() core.AnnotationKeySet {
 	return sv.ConstructsRef
 }
 
diff --git a/pkg/provider/aws/resources/securitygroup.go b/pkg/provider/aws/resources/securitygroup.go
index d506b264..10d97428 100644
--- a/pkg/provider/aws/resources/securitygroup.go
+++ b/pkg/provider/aws/resources/securitygroup.go
@@ -9,7 +9,7 @@ type (
 	SecurityGroup struct {
 		Name          string
 		Vpc           *Vpc
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		IngressRules  []SecurityGroupRule
 		EgressRules   []SecurityGroupRule
 	}
@@ -27,7 +27,7 @@ const SG_TYPE = "security_group"
 
 type SecurityGroupCreateParams struct {
 	AppName string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 }
 
 func (sg *SecurityGroup) Create(dag *core.ResourceGraph, params SecurityGroupCreateParams) error {
@@ -44,7 +44,7 @@ func (sg *SecurityGroup) Create(dag *core.ResourceGraph, params SecurityGroupCre
 	existingSG := dag.GetResource(sg.Id())
 	if existingSG != nil {
 		graphSG := existingSG.(*SecurityGroup)
-		graphSG.ConstructsRef = core.DedupeAnnotationKeys(append(graphSG.ConstructsRef, params.Refs...))
+		graphSG.ConstructsRef.AddAll(params.Refs)
 	}
 	return nil
 }
@@ -99,7 +99,7 @@ func GetSecurityGroup(cfg *config.Application, dag *core.ResourceGraph) *Securit
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (sg *SecurityGroup) KlothoConstructRef() []core.AnnotationKey {
+func (sg *SecurityGroup) KlothoConstructRef() core.AnnotationKeySet {
 	return sg.ConstructsRef
 }
 
diff --git a/pkg/provider/aws/resources/vpc.go b/pkg/provider/aws/resources/vpc.go
index 7d448b1c..146542d6 100644
--- a/pkg/provider/aws/resources/vpc.go
+++ b/pkg/provider/aws/resources/vpc.go
@@ -35,29 +35,29 @@ const (
 type (
 	Vpc struct {
 		Name               string
-		ConstructsRef      []core.AnnotationKey
+		ConstructsRef      core.AnnotationKeySet
 		CidrBlock          string
 		EnableDnsSupport   bool
 		EnableDnsHostnames bool
 	}
 	ElasticIp struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 	}
 	InternetGateway struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		Vpc           *Vpc
 	}
 	NatGateway struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		ElasticIp     *ElasticIp
 		Subnet        *Subnet
 	}
 	Subnet struct {
 		Name                string
-		ConstructsRef       []core.AnnotationKey
+		ConstructsRef       core.AnnotationKeySet
 		CidrBlock           string
 		Vpc                 *Vpc
 		Type                string
@@ -66,7 +66,7 @@ type (
 	}
 	VpcEndpoint struct {
 		Name             string
-		ConstructsRef    []core.AnnotationKey
+		ConstructsRef    core.AnnotationKeySet
 		Vpc              *Vpc
 		Region           *Region
 		ServiceName      string
@@ -77,7 +77,7 @@ type (
 	}
 	RouteTable struct {
 		Name          string
-		ConstructsRef []core.AnnotationKey
+		ConstructsRef core.AnnotationKeySet
 		Vpc           *Vpc
 		Routes        []*RouteTableRoute
 	}
@@ -90,7 +90,7 @@ type (
 
 type VpcCreateParams struct {
 	AppName string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 }
 
 func (vpc *Vpc) Create(dag *core.ResourceGraph, params VpcCreateParams) error {
@@ -101,7 +101,7 @@ func (vpc *Vpc) Create(dag *core.ResourceGraph, params VpcCreateParams) error {
 	existingVpc := dag.GetResource(vpc.Id())
 	if existingVpc != nil {
 		graphVpc := existingVpc.(*Vpc)
-		graphVpc.ConstructsRef = append(graphVpc.KlothoConstructRef(), params.Refs...)
+		graphVpc.ConstructsRef.AddAll(params.Refs)
 	} else {
 		dag.AddResource(vpc)
 	}
@@ -122,16 +122,16 @@ func (vpc *Vpc) Configure(params VpcConfigureParams) error {
 type EipCreateParams struct {
 	AppName string
 	IpName  string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 }
 
 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())
+	existingEip := dag.GetResource(eip.Id())
 	if existingEip != nil {
 		graphEip := existingEip.(*ElasticIp)
-		graphEip.ConstructsRef = append(graphEip.ConstructsRef, params.Refs...)
+		graphEip.ConstructsRef.AddAll(params.Refs)
 	} else {
 		dag.AddResource(eip)
 	}
@@ -140,7 +140,7 @@ func (eip *ElasticIp) Create(dag *core.ResourceGraph, params EipCreateParams) er
 
 type IgwCreateParams struct {
 	AppName string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 }
 
 func (igw *InternetGateway) Create(dag *core.ResourceGraph, params IgwCreateParams) error {
@@ -148,11 +148,11 @@ func (igw *InternetGateway) Create(dag *core.ResourceGraph, params IgwCreatePara
 	igw.Name = igwSanitizer.Apply(fmt.Sprintf("%s-igw", params.AppName))
 	igw.ConstructsRef = params.Refs
 
-	existingIgw := dag.GetResourceByVertexId(igw.Id().String())
+	existingIgw := dag.GetResource(igw.Id())
 
 	if existingIgw != nil {
 		graphIgw := existingIgw.(*InternetGateway)
-		graphIgw.ConstructsRef = append(graphIgw.ConstructsRef, params.Refs...)
+		graphIgw.ConstructsRef.AddAll(params.Refs)
 	} else {
 		err := dag.CreateDependencies(igw, map[string]any{
 			"Vpc": params,
@@ -166,7 +166,7 @@ func (igw *InternetGateway) Create(dag *core.ResourceGraph, params IgwCreatePara
 
 type NatCreateParams struct {
 	AppName string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 	AZ      string
 }
 
@@ -175,10 +175,10 @@ func (nat *NatGateway) Create(dag *core.ResourceGraph, params NatCreateParams) e
 	nat.Name = natGatewaySanitizer.Apply(fmt.Sprintf("%s-%s", params.AppName, params.AZ))
 	nat.ConstructsRef = params.Refs
 
-	existingNat := dag.GetResourceByVertexId(nat.Id().String())
+	existingNat := dag.GetResource(nat.Id())
 	if existingNat != nil {
 		graphNat := existingNat.(*NatGateway)
-		graphNat.ConstructsRef = append(graphNat.ConstructsRef, params.Refs...)
+		graphNat.ConstructsRef.AddAll(params.Refs)
 	} else {
 		subResourceParams := map[string]any{
 			"Subnet": SubnetCreateParams{
@@ -201,7 +201,7 @@ func (nat *NatGateway) Create(dag *core.ResourceGraph, params NatCreateParams) e
 
 type SubnetCreateParams struct {
 	AppName string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 	AZ      string
 	Type    string
 }
@@ -261,10 +261,10 @@ func (subnet *Subnet) Create(dag *core.ResourceGraph, params SubnetCreateParams)
 	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())
+	existingSubnet := dag.GetResource(subnet.Id())
 	if existingSubnet != nil {
 		graphSubnet := existingSubnet.(*Subnet)
-		graphSubnet.ConstructsRef = core.DedupeAnnotationKeys(append(graphSubnet.ConstructsRef, params.Refs...))
+		graphSubnet.ConstructsRef.AddAll(params.Refs)
 	}
 	return nil
 }
@@ -294,7 +294,7 @@ func (subnet *Subnet) Configure(params SubnetConfigureParams) error {
 type RouteTableCreateParams struct {
 	AppName string
 	Name    string
-	Refs    []core.AnnotationKey
+	Refs    core.AnnotationKeySet
 }
 
 func (rt *RouteTable) Create(dag *core.ResourceGraph, params RouteTableCreateParams) error {
@@ -314,10 +314,10 @@ func (rt *RouteTable) Create(dag *core.ResourceGraph, params RouteTableCreatePar
 	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())
+	existingRt := dag.GetResource(rt.Id())
 	if existingRt != nil {
 		graphRt := existingRt.(*RouteTable)
-		graphRt.ConstructsRef = core.DedupeAnnotationKeys(append(graphRt.ConstructsRef, params.Refs...))
+		graphRt.ConstructsRef.AddAll(params.Refs)
 	}
 	return nil
 }
@@ -553,7 +553,7 @@ func NewElasticIp(appName string, ipName string) *ElasticIp {
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (subnet *ElasticIp) KlothoConstructRef() []core.AnnotationKey {
+func (subnet *ElasticIp) KlothoConstructRef() core.AnnotationKeySet {
 	return subnet.ConstructsRef
 }
 
@@ -574,7 +574,7 @@ func NewInternetGateway(appName string, igwName string, vpc *Vpc) *InternetGatew
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (igw *InternetGateway) KlothoConstructRef() []core.AnnotationKey {
+func (igw *InternetGateway) KlothoConstructRef() core.AnnotationKeySet {
 	return igw.ConstructsRef
 }
 
@@ -596,7 +596,7 @@ func NewNatGateway(appName string, natGatewayName string, subnet *Subnet, ip *El
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (natGateway *NatGateway) KlothoConstructRef() []core.AnnotationKey {
+func (natGateway *NatGateway) KlothoConstructRef() core.AnnotationKeySet {
 	return natGateway.ConstructsRef
 }
 
@@ -625,7 +625,7 @@ func NewSubnet(subnetName string, vpc *Vpc, cidrBlock string, subnetType string,
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (subnet *Subnet) KlothoConstructRef() []core.AnnotationKey {
+func (subnet *Subnet) KlothoConstructRef() core.AnnotationKeySet {
 	return subnet.ConstructsRef
 }
 
@@ -663,7 +663,7 @@ func NewVpcEndpoint(service string, vpc *Vpc, endpointType string, region *Regio
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (vpce *VpcEndpoint) KlothoConstructRef() []core.AnnotationKey {
+func (vpce *VpcEndpoint) KlothoConstructRef() core.AnnotationKeySet {
 	return vpce.ConstructsRef
 }
 
@@ -686,7 +686,7 @@ func NewVpc(appName string) *Vpc {
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (vpc *Vpc) KlothoConstructRef() []core.AnnotationKey {
+func (vpc *Vpc) KlothoConstructRef() core.AnnotationKeySet {
 	return vpc.ConstructsRef
 }
 
@@ -700,7 +700,7 @@ func (vpc *Vpc) Id() core.ResourceId {
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (rt *RouteTable) KlothoConstructRef() []core.AnnotationKey {
+func (rt *RouteTable) KlothoConstructRef() core.AnnotationKeySet {
 	return rt.ConstructsRef
 }
 
diff --git a/pkg/provider/aws/secret.go b/pkg/provider/aws/secret.go
index fdcb1503..e8667e47 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), []core.AnnotationKey{construct.Provenance()}, policyDoc)
+		policy := resources.NewIamInlinePolicy(fmt.Sprintf("%s-secretsmanager", secret.Name), core.AnnotationKeySetOf(construct.Provenance()), policyDoc)
 		a.PolicyGenerator.AddInlinePolicyToUnit(unit.Id(), policy)
 	}
 	return nil
diff --git a/pkg/provider/imports/resource.go b/pkg/provider/imports/resource.go
index 4a773af7..b01806f2 100644
--- a/pkg/provider/imports/resource.go
+++ b/pkg/provider/imports/resource.go
@@ -13,7 +13,7 @@ type Imported struct {
 	ID string
 }
 
-func (imp Imported) KlothoConstructRef() []core.AnnotationKey {
+func (imp Imported) KlothoConstructRef() core.AnnotationKeySet {
 	return nil
 }
 
diff --git a/pkg/visualizer/file.go b/pkg/visualizer/file.go
index 84fd1b04..1b3d0eb7 100644
--- a/pkg/visualizer/file.go
+++ b/pkg/visualizer/file.go
@@ -64,16 +64,11 @@ func (f *File) WriteTo(w io.Writer) (n int64, err error) {
 	wh.Writef("  provider: %s\n", f.Provider)
 	wh.Write("  resources:\n")
 
-	resourceIds, err := f.DAG.TopologicalSort()
+	resources, err := f.DAG.ReverseTopologicalSort()
 	if err != nil {
 		return
 	}
-	for i := len(resourceIds)/2 - 1; i >= 0; i-- {
-		opp := len(resourceIds) - 1 - i
-		resourceIds[i], resourceIds[opp] = resourceIds[opp], resourceIds[i]
-	}
-	for _, resourceId := range resourceIds {
-		resource := f.DAG.GetResourceByVertexId(resourceId)
+	for _, resource := range resources {
 		if resource.Id().Provider == core.InternalProvider {
 			// Don't show internal resources such as imported in the topology
 			// TODO maybe make some way of indicating imported resources in the visualizer
@@ -83,7 +78,7 @@ func (f *File) WriteTo(w io.Writer) (n int64, err error) {
 		if key == "" {
 			continue
 		}
-		wh.Writef(indent+"%s: # %s\n", key, resourceId)
+		wh.Writef(indent+"%s: # %s\n", key, resource.Id())
 		properties := propFetcher.apply(resource, f.DAG)
 		if len(properties) > 0 {
 			writeYaml(properties, 2, wh)


Test patch:
diff --git a/pkg/core/resource_graph_test.go b/pkg/core/resource_graph_test.go
index f8bf6c53..f3fa8ed7 100644
--- a/pkg/core/resource_graph_test.go
+++ b/pkg/core/resource_graph_test.go
@@ -37,7 +37,7 @@ type testResource struct {
 }
 
 // KlothoConstructRef returns AnnotationKey of the klotho resource the cloud resource is correlated to
-func (tr *testResource) KlothoConstructRef() []AnnotationKey {
+func (tr *testResource) KlothoConstructRef() AnnotationKeySet {
 	return nil
 }
 
@@ -129,9 +129,9 @@ func TestResourceGraph_AddDependenciesReflect(t *testing.T) {
 		"nested_arr1", "nested_arr2",
 		"nested_map1", "nested_map2",
 	} {
-		assert.NotNil(dag.GetDependencyByVertexIds(tr.Id().String(), (&testResource{Name: target}).Id().String()), "source -> %s", target)
+		assert.NotNil(dag.GetDependency(tr.Id(), (&testResource{Name: target}).Id()), "source -> %s", target)
 	}
-	assert.Nil(dag.GetDependencyByVertexIds(tr.Id().String(), (&testResource{Name: "nested_single"}).Id().String()))
+	assert.Nil(dag.GetDependency(tr.Id(), (&testResource{Name: "nested_single"}).Id()))
 }
 
 func TestResourceGraph_CreateDependencies(t *testing.T) {
@@ -254,6 +254,6 @@ func TestResourceGraph_CreateDependencies(t *testing.T) {
 		"nested_arr1", "nested_arr2",
 		"nested_map1", "nested_map2",
 	} {
-		assert.Nil(dag.GetDependencyByVertexIds(tr.Id().String(), (&testResource{Name: target}).Id().String()), "source -> %s", target)
+		assert.Nil(dag.GetDependency(tr.Id(), (&testResource{Name: target}).Id()), "source -> %s", target)
 	}
 }
diff --git a/pkg/infra/iac2/templates_compiler_test.go b/pkg/infra/iac2/templates_compiler_test.go
index e4abfe7b..b9ffd0a0 100644
--- a/pkg/infra/iac2/templates_compiler_test.go
+++ b/pkg/infra/iac2/templates_compiler_test.go
@@ -390,21 +390,21 @@ type (
 	}
 )
 
-func (f *DummyFizz) Id() core.ResourceId                      { return core.ResourceId{Name: "fizz-" + f.Value} }
-func (f *DummyFizz) Provider() string                         { return "DummyProvider" }
-func (f *DummyFizz) KlothoConstructRef() []core.AnnotationKey { return nil }
+func (f *DummyFizz) Id() core.ResourceId                       { return core.ResourceId{Name: "fizz-" + f.Value} }
+func (f *DummyFizz) Provider() string                          { return "DummyProvider" }
+func (f *DummyFizz) KlothoConstructRef() core.AnnotationKeySet { return nil }
 
-func (b DummyBuzz) Id() core.ResourceId                      { return core.ResourceId{Name: "buzz-shared"} }
-func (f DummyBuzz) Provider() string                         { return "DummyProvider" }
-func (f DummyBuzz) KlothoConstructRef() []core.AnnotationKey { return nil }
+func (b DummyBuzz) Id() core.ResourceId                       { return core.ResourceId{Name: "buzz-shared"} }
+func (f DummyBuzz) Provider() string                          { return "DummyProvider" }
+func (f DummyBuzz) KlothoConstructRef() core.AnnotationKeySet { return nil }
 
-func (p *DummyBig) Id() core.ResourceId                      { return core.ResourceId{Name: "big-" + p.id} }
-func (f *DummyBig) Provider() string                         { return "DummyProvider" }
-func (f *DummyBig) KlothoConstructRef() []core.AnnotationKey { return nil }
+func (p *DummyBig) Id() core.ResourceId                       { return core.ResourceId{Name: "big-" + p.id} }
+func (f *DummyBig) Provider() string                          { return "DummyProvider" }
+func (f *DummyBig) KlothoConstructRef() core.AnnotationKeySet { return nil }
 
-func (p DummyVoid) Id() core.ResourceId                      { return core.ResourceId{Name: "void"} }
-func (f DummyVoid) Provider() string                         { return "DummyProvider" }
-func (f DummyVoid) KlothoConstructRef() []core.AnnotationKey { return nil }
+func (p DummyVoid) Id() core.ResourceId                       { return core.ResourceId{Name: "void"} }
+func (f DummyVoid) Provider() string                          { return "DummyProvider" }
+func (f DummyVoid) KlothoConstructRef() core.AnnotationKeySet { return nil }
 
 var dummyTemplateFiles = map[string]string{
 	`dummy_fizz/factory.ts`: `
diff --git a/pkg/knowledge_base/edge_kb_test.go b/pkg/knowledge_base/edge_kb_test.go
index 9a429077..82015e34 100644
--- a/pkg/knowledge_base/edge_kb_test.go
+++ b/pkg/knowledge_base/edge_kb_test.go
@@ -208,17 +208,17 @@ type (
 	}
 )
 
-func (f *A) Id() core.ResourceId                      { return core.ResourceId{Name: "A" + f.Name} }
-func (f *A) KlothoConstructRef() []core.AnnotationKey { return nil }
+func (f *A) Id() core.ResourceId                       { return core.ResourceId{Name: "A" + f.Name} }
+func (f *A) KlothoConstructRef() core.AnnotationKeySet { return nil }
 
-func (b B) Id() core.ResourceId                      { return core.ResourceId{Name: "B" + b.Name} }
-func (f B) KlothoConstructRef() []core.AnnotationKey { return nil }
+func (b B) Id() core.ResourceId                       { return core.ResourceId{Name: "B" + b.Name} }
+func (f B) KlothoConstructRef() core.AnnotationKeySet { 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 *C) Id() core.ResourceId                       { return core.ResourceId{Name: "C" + p.Name} }
+func (f *C) KlothoConstructRef() core.AnnotationKeySet { 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 *D) Id() core.ResourceId                       { return core.ResourceId{Name: "D" + p.Name} }
+func (f *D) KlothoConstructRef() core.AnnotationKeySet { return nil }
 
-func (p *E) Id() core.ResourceId                      { return core.ResourceId{Name: "E" + p.Name} }
-func (f *E) KlothoConstructRef() []core.AnnotationKey { return nil }
+func (p *E) Id() core.ResourceId                       { return core.ResourceId{Name: "E" + p.Name} }
+func (f *E) KlothoConstructRef() core.AnnotationKeySet { return nil }
diff --git a/pkg/provider/aws/content_delivery_network_test.go b/pkg/provider/aws/content_delivery_network_test.go
index f9dff019..5ac837f1 100644
--- a/pkg/provider/aws/content_delivery_network_test.go
+++ b/pkg/provider/aws/content_delivery_network_test.go
@@ -26,7 +26,7 @@ func Test_createCDNs(t *testing.T) {
 		constructIdToResources map[string][]core.Resource
 		cfg                    config.Application
 		want                   coretesting.ResourcesExpectation
-		wantRefs               map[core.ResourceId][]core.AnnotationKey
+		wantRefs               map[core.ResourceId]core.AnnotationKeySet
 		wantErr                bool
 	}{
 		{
@@ -61,8 +61,8 @@ func Test_createCDNs(t *testing.T) {
 					{Source: "aws:s3_bucket_policy:test-su-su", Destination: "aws:s3_bucket:test-su"},
 				},
 			},
-			wantRefs: map[core.ResourceId][]core.AnnotationKey{
-				distro.Id(): {su.Provenance()},
+			wantRefs: map[core.ResourceId]core.AnnotationKeySet{
+				distro.Id(): core.AnnotationKeySetOf(su.Provenance()),
 			},
 		},
 		{
@@ -92,8 +92,8 @@ func Test_createCDNs(t *testing.T) {
 					{Source: "aws:cloudfront_distribution:test-cdn", Destination: "aws:api_stage:test-gw-stage"},
 				},
 			},
-			wantRefs: map[core.ResourceId][]core.AnnotationKey{
-				distro.Id(): {gw.Provenance()},
+			wantRefs: map[core.ResourceId]core.AnnotationKeySet{
+				distro.Id(): core.AnnotationKeySetOf(gw.Provenance()),
 			},
 		},
 		{
@@ -139,9 +139,9 @@ func Test_createCDNs(t *testing.T) {
 					{Source: "aws:s3_bucket_policy:test-su-su", Destination: "aws:s3_bucket:test-su"},
 				},
 			},
-			wantRefs: map[core.ResourceId][]core.AnnotationKey{
-				distro.Id():  {su.Provenance()},
-				distro2.Id(): {gw.Provenance()},
+			wantRefs: map[core.ResourceId]core.AnnotationKeySet{
+				distro.Id():  core.AnnotationKeySetOf(su.Provenance()),
+				distro2.Id(): core.AnnotationKeySetOf(gw.Provenance()),
 			},
 		},
 	}
@@ -176,7 +176,7 @@ func Test_createCDNs(t *testing.T) {
 			tt.want.Assert(t, dag)
 
 			for key, val := range tt.wantRefs {
-				assert.ElementsMatch(val, dag.GetResource(key).KlothoConstructRef())
+				assert.Equal(val, dag.GetResource(key).KlothoConstructRef())
 			}
 		})
 
diff --git a/pkg/provider/aws/execution_unit_test.go b/pkg/provider/aws/execution_unit_test.go
index c062cb52..12ada2dc 100644
--- a/pkg/provider/aws/execution_unit_test.go
+++ b/pkg/provider/aws/execution_unit_test.go
@@ -300,7 +300,7 @@ func Test_handleHelmChartAwsValues(t *testing.T) {
 				params: map[string]any{
 					"IMAGE": resources.ImageCreateParams{
 						AppName: config.AppName,
-						Refs:    []core.AnnotationKey{eu.AnnotationKey},
+						Refs:    core.AnnotationKeySetOf(eu.AnnotationKey),
 						Name:    eu.ID,
 					},
 				},
@@ -324,7 +324,7 @@ func Test_handleHelmChartAwsValues(t *testing.T) {
 				params: map[string]any{
 					"SERVICEACCOUNT": resources.RoleCreateParams{
 						Name:    fmt.Sprintf("%s-%s-ExecutionRole", config.AppName, eu.ID),
-						Refs:    []core.AnnotationKey{eu.AnnotationKey},
+						Refs:    core.AnnotationKeySetOf(eu.AnnotationKey),
 						AppName: config.AppName,
 					},
 				},
@@ -368,7 +368,7 @@ func Test_handleHelmChartAwsValues(t *testing.T) {
 						InstanceType: "t3.medium",
 						ClusterName:  "cluster1",
 						AppName:      config.AppName,
-						Refs:         []core.AnnotationKey{eu.AnnotationKey},
+						Refs:         core.AnnotationKeySetOf(eu.AnnotationKey),
 					},
 				},
 				values: map[string]any{
@@ -410,7 +410,7 @@ func Test_handleExecUnitProxy(t *testing.T) {
 	unit2 := &core.ExecutionUnit{AnnotationKey: core.AnnotationKey{ID: "unit2"}}
 	chart := &kubernetes.HelmChart{
 		Name:          "chart",
-		ConstructRefs: []core.AnnotationKey{unit1.AnnotationKey, unit2.AnnotationKey},
+		ConstructRefs: core.AnnotationKeySetOf(unit1.AnnotationKey, unit2.AnnotationKey),
 		ExecutionUnits: []*kubernetes.HelmExecUnit{
 			{Name: unit1.ID},
 			{Name: unit2.ID},
@@ -470,7 +470,7 @@ func Test_handleExecUnitProxy(t *testing.T) {
 			},
 			config: config.Application{AppName: "test", Defaults: config.Defaults{ExecutionUnit: config.KindDefaults{Type: kubernetes.KubernetesType}}},
 			existingResources: []core.Resource{
-				&resources.EksCluster{Name: "cluster", ConstructsRef: []core.AnnotationKey{unit1.AnnotationKey, unit2.AnnotationKey}},
+				&resources.EksCluster{Name: "cluster", ConstructsRef: core.AnnotationKeySetOf(unit1.AnnotationKey, unit2.AnnotationKey)},
 				chart},
 			want: coretesting.ResourcesExpectation{
 				Nodes: []string{
@@ -613,7 +613,7 @@ func Test_convertExecUnitParams(t *testing.T) {
 						Key:                 "BUCKETBUCKETNAME",
 					},
 				},
-				ConstructRefs: []core.AnnotationKey{{ID: "unit"}},
+				ConstructRefs: core.AnnotationKeySetOf(core.AnnotationKey{ID: "unit"}),
 				Values:        make(map[string]any),
 			},
 			wants: resources.EnvironmentVariables{
diff --git a/pkg/provider/aws/knowledgebase/edge_configure_test.go b/pkg/provider/aws/knowledgebase/edge_configure_test.go
index 8c9a159e..7d01c39f 100644
--- a/pkg/provider/aws/knowledgebase/edge_configure_test.go
+++ b/pkg/provider/aws/knowledgebase/edge_configure_test.go
@@ -42,7 +42,7 @@ func Test_ConfigureEdge(t *testing.T) {
 					InlinePolicies: []*resources.IamInlinePolicy{
 						{
 							Name:          "rds-connectionpolicy",
-							ConstructsRef: []core.AnnotationKey{},
+							ConstructsRef: core.AnnotationKeySetOf(),
 							Policy: &resources.PolicyDocument{
 								Version: "2012-10-17",
 								Statement: []resources.StatementEntry{
@@ -127,7 +127,7 @@ func Test_ConfigureEdge(t *testing.T) {
 						InlinePolicies: []*resources.IamInlinePolicy{
 							{
 								Name:          "instance-connectionpolicy",
-								ConstructsRef: []core.AnnotationKey{},
+								ConstructsRef: core.AnnotationKeySetOf(),
 								Policy: &resources.PolicyDocument{
 									Version: "2012-10-17",
 									Statement: []resources.StatementEntry{
diff --git a/pkg/provider/aws/plugin_test.go b/pkg/provider/aws/plugin_test.go
index 0c1670e4..4c71217f 100644
--- a/pkg/provider/aws/plugin_test.go
+++ b/pkg/provider/aws/plugin_test.go
@@ -184,7 +184,7 @@ func Test_CopyConstructEdgesToDag(t *testing.T) {
 				return
 			}
 			for _, dep := range tt.want {
-				edge := dag.GetDependency(dep.Source, dep.Destination)
+				edge := dag.GetDependency(dep.Source.Id(), dep.Destination.Id())
 				assert.Equal(edge, dep)
 			}
 		})
@@ -214,9 +214,12 @@ func Test_configureResources(t *testing.T) {
 					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"}},
+			resources: []core.Resource{
+				&resources.LambdaFunction{Name: "lambda", ConstructsRef: core.AnnotationKeySetOf(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.LambdaFunction{Name: "lambda", Timeout: 100, MemorySize: 200, ConstructsRef: core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}), EnvironmentVariables: resources.EnvironmentVariables{"env1": core.IaCValue{Property: "val1"}}},
 				&resources.RdsProxy{Name: "rds", EngineFamily: "POSTGRESQL", IdleClientTimeout: 1800}},
 		},
 	}
diff --git a/pkg/provider/aws/provider_test.go b/pkg/provider/aws/provider_test.go
index 9168a85f..eb6ad95f 100644
--- a/pkg/provider/aws/provider_test.go
+++ b/pkg/provider/aws/provider_test.go
@@ -75,7 +75,7 @@ type (
 	dummyConstruct string
 )
 
-func (dr dummyResource) KlothoConstructRef() []core.AnnotationKey { return nil }
+func (dr dummyResource) KlothoConstructRef() core.AnnotationKeySet { return nil }
 
 func (dr dummyResource) Id() core.ResourceId {
 	return core.ResourceId{Provider: "test", Type: "", Name: string(dr)}
diff --git a/pkg/provider/aws/resources/api_gateway_test.go b/pkg/provider/aws/resources/api_gateway_test.go
index d358a728..c9e023c4 100644
--- a/pkg/provider/aws/resources/api_gateway_test.go
+++ b/pkg/provider/aws/resources/api_gateway_test.go
@@ -10,7 +10,7 @@ import (
 )
 
 func Test_RestApiCreate(t *testing.T) {
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name    string
 		api     *RestApi
@@ -57,7 +57,7 @@ func Test_RestApiCreate(t *testing.T) {
 			}
 			metadata := RestApiCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Refs:    core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}),
 				Name:    "rest-api",
 			}
 			if tt.apiName != "" {
@@ -79,9 +79,10 @@ func Test_RestApiCreate(t *testing.T) {
 
 			assert.Equal(api.Name, "my-app-rest-api")
 			if tt.api == nil {
-				assert.ElementsMatch(api.ConstructsRef, metadata.Refs)
+				assert.Equal(api.ConstructsRef, metadata.Refs)
 			} else {
-				assert.ElementsMatch(api.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				initialRefs.Add(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability})
+				assert.Equal(api.KlothoConstructRef(), initialRefs)
 			}
 		})
 	}
@@ -123,7 +124,7 @@ func Test_RestApiConfigure(t *testing.T) {
 }
 
 func Test_ApiResourceCreate(t *testing.T) {
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []coretesting.CreateCase[ApiResourceCreateParams, *ApiResource]{
 		{
 			Name: "nil resource",
@@ -148,7 +149,7 @@ func Test_ApiResourceCreate(t *testing.T) {
 			Check: func(assert *assert.Assertions, resource *ApiResource) {
 				assert.Equal("my-app-/my/api/route", resource.Name)
 				assert.Equal(resource.PathPart, "route")
-				assert.ElementsMatch(resource.ConstructsRef, []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}})
+				assert.Equal(resource.ConstructsRef, core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
 			},
 		},
 		{
@@ -165,7 +166,8 @@ func Test_ApiResourceCreate(t *testing.T) {
 			},
 			Check: func(assert *assert.Assertions, resource *ApiResource) {
 				assert.Equal("my-app-/my/api/route", resource.Name)
-				assert.ElementsMatch(resource.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				expect := initialRefs.CloneWith(core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				assert.Equal(resource.KlothoConstructRef(), expect)
 
 			},
 		},
@@ -195,14 +197,14 @@ func Test_ApiResourceCreate(t *testing.T) {
 			Check: func(assert *assert.Assertions, resource *ApiResource) {
 				assert.Equal("my-app-/my/-api/route/-method", resource.Name)
 				assert.Equal(resource.PathPart, "{method}")
-				assert.ElementsMatch(resource.ConstructsRef, []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}})
+				assert.Equal(resource.ConstructsRef, core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
 			},
 		},
 	}
 	for _, tt := range cases {
 		t.Run(tt.Name, func(t *testing.T) {
 			tt.Params.AppName = "my-app"
-			tt.Params.Refs = []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}}
+			tt.Params.Refs = core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability})
 			tt.Params.ApiName = "my-api"
 			tt.Run(t)
 		})
@@ -210,7 +212,7 @@ func Test_ApiResourceCreate(t *testing.T) {
 }
 
 func Test_ApiIntegrationCreate(t *testing.T) {
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name        string
 		integration *ApiIntegration
@@ -261,7 +263,7 @@ func Test_ApiIntegrationCreate(t *testing.T) {
 			}
 			metadata := ApiIntegrationCreateParams{
 				AppName:    "my-app",
-				Refs:       []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Refs:       core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}),
 				Path:       "/my/api/route",
 				ApiName:    "my-api",
 				HttpMethod: "post",
@@ -284,16 +286,17 @@ func Test_ApiIntegrationCreate(t *testing.T) {
 				assert.NotNil(integration.RestApi)
 				assert.NotNil(integration.Method)
 				assert.NotNil(integration.Resource)
-				assert.ElementsMatch(integration.ConstructsRef, metadata.Refs)
+				assert.Equal(integration.ConstructsRef, metadata.Refs)
 			} else {
-				assert.ElementsMatch(integration.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				expect := initialRefs.CloneWith(core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				assert.Equal(integration.KlothoConstructRef(), expect)
 			}
 		})
 	}
 }
 
 func Test_ApiMethodCreate(t *testing.T) {
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name   string
 		method *ApiMethod
@@ -340,7 +343,7 @@ func Test_ApiMethodCreate(t *testing.T) {
 			}
 			metadata := ApiMethodCreateParams{
 				AppName:    "my-app",
-				Refs:       []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Refs:       core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}),
 				Path:       "/my/api/route",
 				ApiName:    "my-api",
 				HttpMethod: "post",
@@ -361,9 +364,10 @@ func Test_ApiMethodCreate(t *testing.T) {
 			if tt.method == nil {
 				assert.NotNil(method.RestApi)
 				assert.NotNil(method.Resource)
-				assert.ElementsMatch(method.ConstructsRef, metadata.Refs)
+				assert.Equal(method.ConstructsRef, metadata.Refs)
 			} else {
-				assert.ElementsMatch(method.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				expect := initialRefs.CloneWith(core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				assert.Equal(method.KlothoConstructRef(), expect)
 			}
 		})
 	}
@@ -405,7 +409,7 @@ func Test_ApiMethodConfigure(t *testing.T) {
 }
 
 func Test_ApiDeploymentCreate(t *testing.T) {
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name       string
 		deployment *ApiDeployment
@@ -443,7 +447,7 @@ func Test_ApiDeploymentCreate(t *testing.T) {
 			}
 			metadata := ApiDeploymentCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Refs:    core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}),
 				Name:    "deployment",
 			}
 
@@ -461,16 +465,17 @@ func Test_ApiDeploymentCreate(t *testing.T) {
 			assert.Equal(deployment.Name, "my-app-deployment")
 			if tt.deployment == nil {
 				assert.NotNil(deployment.RestApi)
-				assert.ElementsMatch(deployment.ConstructsRef, metadata.Refs)
+				assert.Equal(deployment.ConstructsRef, metadata.Refs)
 			} else {
-				assert.ElementsMatch(deployment.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				expect := initialRefs.CloneWith(core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				assert.Equal(deployment.KlothoConstructRef(), expect)
 			}
 		})
 	}
 }
 
 func Test_ApiStageCreate(t *testing.T) {
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name  string
 		stage *ApiStage
@@ -511,7 +516,7 @@ func Test_ApiStageCreate(t *testing.T) {
 			}
 			metadata := ApiStageCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Refs:    core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}),
 				Name:    "stage",
 			}
 
@@ -529,9 +534,10 @@ func Test_ApiStageCreate(t *testing.T) {
 			assert.Equal(stage.Name, "my-app-stage")
 			if tt.stage == nil {
 				assert.NotNil(stage.RestApi)
-				assert.ElementsMatch(stage.ConstructsRef, metadata.Refs)
+				assert.Equal(stage.ConstructsRef, metadata.Refs)
 			} else {
-				assert.ElementsMatch(stage.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				expect := initialRefs.CloneWith(core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				assert.Equal(stage.KlothoConstructRef(), expect)
 			}
 		})
 	}
diff --git a/pkg/provider/aws/resources/cloudfront_test.go b/pkg/provider/aws/resources/cloudfront_test.go
index 6a90f542..02267835 100644
--- a/pkg/provider/aws/resources/cloudfront_test.go
+++ b/pkg/provider/aws/resources/cloudfront_test.go
@@ -5,11 +5,11 @@ import (
 	"testing"
 
 	"github.com/klothoplatform/klotho/pkg/core"
+	"github.com/klothoplatform/klotho/pkg/core/coretesting"
 	"github.com/stretchr/testify/assert"
 )
 
 func Test_CreateS3Origin(t *testing.T) {
-
 	unit := &core.StaticUnit{AnnotationKey: core.AnnotationKey{ID: "test"}}
 	bucket := NewS3Bucket(&core.Fs{AnnotationKey: core.AnnotationKey{ID: "bucket"}}, "test")
 	distro := NewCloudfrontDistribution("test", "1")
@@ -18,9 +18,20 @@ func Test_CreateS3Origin(t *testing.T) {
 	dag := core.NewResourceGraph()
 	CreateS3Origin(unit, bucket, distro, dag)
 
-	assert.NotNil(dag.GetDependencyByVertexIds("aws:s3_bucket_policy:test-bucket-test", "aws:cloudfront_origin_access_identity:test-bucket-test"))
-	assert.NotNil(dag.GetDependencyByVertexIds("aws:s3_bucket_policy:test-bucket-test", "aws:s3_bucket:test-bucket"))
-	assert.NotNil(dag.GetDependencyByVertexIds("aws:cloudfront_distribution:test-1", "aws:cloudfront_origin_access_identity:test-bucket-test"))
+	want := coretesting.ResourcesExpectation{
+		Nodes: []string{
+			"aws:cloudfront_distribution:test-1",
+			"aws:cloudfront_origin_access_identity:test-bucket-test",
+			"aws:s3_bucket:test-bucket",
+			"aws:s3_bucket_policy:test-bucket-test",
+		},
+		Deps: []coretesting.StringDep{
+			{Source: "aws:cloudfront_distribution:test-1", Destination: "aws:cloudfront_origin_access_identity:test-bucket-test"},
+			{Source: "aws:s3_bucket_policy:test-bucket-test", Destination: "aws:cloudfront_origin_access_identity:test-bucket-test"},
+			{Source: "aws:s3_bucket_policy:test-bucket-test", Destination: "aws:s3_bucket:test-bucket"},
+		},
+	}
+	want.Assert(t, dag)
 
 	oai := dag.GetResource(core.ResourceId{Provider: "aws", Type: "cloudfront_origin_access_identity", Name: fmt.Sprintf("%s-%s", bucket.Name, unit.ID)})
 	if !assert.NotNil(oai) {
@@ -52,7 +63,9 @@ func Test_CreateS3Origin(t *testing.T) {
 		},
 	})
 
-	assert.Len(distro.Origins, 1)
+	if !assert.Len(distro.Origins, 1) {
+		return
+	}
 	s3Origin := distro.Origins[0]
 	assert.Equal(s3Origin.DomainName, core.IaCValue{
 		Resource: bucket,
diff --git a/pkg/provider/aws/resources/cloudwatch_test.go b/pkg/provider/aws/resources/cloudwatch_test.go
index 844805b3..fa899354 100644
--- a/pkg/provider/aws/resources/cloudwatch_test.go
+++ b/pkg/provider/aws/resources/cloudwatch_test.go
@@ -10,7 +10,7 @@ import (
 )
 
 func Test_CloudwatchLogGroupCreate(t *testing.T) {
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name     string
 		logGroup *LogGroup
@@ -45,7 +45,7 @@ func Test_CloudwatchLogGroupCreate(t *testing.T) {
 			}
 			metadata := CloudwatchLogGroupCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Refs:    core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}),
 				Name:    "log-group",
 			}
 
@@ -58,15 +58,16 @@ func Test_CloudwatchLogGroupCreate(t *testing.T) {
 
 			tt.want.Assert(t, dag)
 
-			graphLogGroup := dag.GetResourceByVertexId(logGroup.Id().String())
+			graphLogGroup := dag.GetResource(logGroup.Id())
 			logGroup = graphLogGroup.(*LogGroup)
 
 			assert.Equal(logGroup.Name, "my-app-log-group")
 			if tt.logGroup == nil {
-				assert.ElementsMatch(logGroup.ConstructsRef, metadata.Refs)
+				assert.Equal(logGroup.ConstructsRef, metadata.Refs)
 			} else {
 				assert.Equal(logGroup, tt.logGroup)
-				assert.ElementsMatch(logGroup.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				expect := initialRefs.CloneWith(core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				assert.Equal(logGroup.KlothoConstructRef(), expect)
 			}
 		})
 	}
diff --git a/pkg/provider/aws/resources/dynamodb_test.go b/pkg/provider/aws/resources/dynamodb_test.go
index 30f2cd09..9a4fa4be 100644
--- a/pkg/provider/aws/resources/dynamodb_test.go
+++ b/pkg/provider/aws/resources/dynamodb_test.go
@@ -1,9 +1,10 @@
 package resources
 
 import (
+	"testing"
+
 	"github.com/klothoplatform/klotho/pkg/annotation"
 	"github.com/klothoplatform/klotho/pkg/core/coretesting"
-	"testing"
 
 	"github.com/klothoplatform/klotho/pkg/core"
 	"github.com/stretchr/testify/assert"
@@ -22,12 +23,12 @@ func Test_DynamodbTableCreate(t *testing.T) {
 			},
 			Check: func(assert *assert.Assertions, table *DynamodbTable) {
 				assert.Equal(table.Name, "my-app-kv")
-				assert.ElementsMatch(table.ConstructsRef, []core.AnnotationKey{kv.AnnotationKey})
+				assert.Equal(table.ConstructsRef, core.AnnotationKeySetOf(kv.AnnotationKey))
 			},
 		},
 		{
 			Name:     "existing dynamodb table",
-			Existing: &DynamodbTable{Name: "my-app-kv", ConstructsRef: []core.AnnotationKey{existingKey}},
+			Existing: &DynamodbTable{Name: "my-app-kv", ConstructsRef: core.AnnotationKeySetOf(existingKey)},
 			Want: coretesting.ResourcesExpectation{
 				Nodes: []string{
 					"aws:dynamodb_table:my-app-kv",
@@ -35,7 +36,7 @@ func Test_DynamodbTableCreate(t *testing.T) {
 			},
 			Check: func(assert *assert.Assertions, table *DynamodbTable) {
 				assert.Equal(table.Name, "my-app-kv")
-				assert.ElementsMatch(table.ConstructsRef, []core.AnnotationKey{kv.AnnotationKey, existingKey})
+				assert.Equal(table.ConstructsRef, core.AnnotationKeySetOf(kv.AnnotationKey, existingKey))
 			},
 		},
 	}
@@ -43,7 +44,7 @@ func Test_DynamodbTableCreate(t *testing.T) {
 		t.Run(tt.Name, func(t *testing.T) {
 			tt.Params = DynamodbTableCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{kv.AnnotationKey},
+				Refs:    core.AnnotationKeySetOf(kv.AnnotationKey),
 				Name:    "kv",
 			}
 
diff --git a/pkg/provider/aws/resources/ecr_test.go b/pkg/provider/aws/resources/ecr_test.go
index 4f94ff3e..2a29b1d3 100644
--- a/pkg/provider/aws/resources/ecr_test.go
+++ b/pkg/provider/aws/resources/ecr_test.go
@@ -10,7 +10,7 @@ import (
 )
 
 func Test_EcrRepositoryCreate(t *testing.T) {
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name string
 		repo *EcrRepository
@@ -45,7 +45,7 @@ func Test_EcrRepositoryCreate(t *testing.T) {
 			}
 			metadata := RepoCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Refs:    core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}),
 			}
 
 			repo := &EcrRepository{}
@@ -60,17 +60,18 @@ func Test_EcrRepositoryCreate(t *testing.T) {
 			repo = graphRepo.(*EcrRepository)
 			assert.Equal(repo.Name, "my-app")
 			if tt.repo == nil {
-				assert.ElementsMatch(repo.ConstructsRef, metadata.Refs)
+				assert.Equal(repo.ConstructsRef, metadata.Refs)
 			} else {
 				assert.Equal(repo, tt.repo)
-				assert.ElementsMatch(repo.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				expect := initialRefs.CloneWith(core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				assert.Equal(repo.KlothoConstructRef(), expect)
 			}
 		})
 	}
 }
 
 func Test_EcrImageCreate(t *testing.T) {
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name    string
 		image   *EcrImage
@@ -104,7 +105,7 @@ func Test_EcrImageCreate(t *testing.T) {
 			}
 			metadata := ImageCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Refs:    core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}),
 				Name:    "test-unit",
 			}
 			image := &EcrImage{}
diff --git a/pkg/provider/aws/resources/eks_test.go b/pkg/provider/aws/resources/eks_test.go
index a15f7ac6..3842a508 100644
--- a/pkg/provider/aws/resources/eks_test.go
+++ b/pkg/provider/aws/resources/eks_test.go
@@ -11,7 +11,7 @@ import (
 
 func Test_EksClusterCreate(t *testing.T) {
 	eu := &core.ExecutionUnit{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}}
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []coretesting.CreateCase[EksClusterCreateParams, *EksCluster]{
 		{
 			Name: "nil lambda",
@@ -75,7 +75,7 @@ func Test_EksClusterCreate(t *testing.T) {
 				assert.Equal(cluster.Name, "my-app-cluster")
 				assert.NotNil(cluster.Vpc)
 				assert.Len(cluster.SecurityGroups, 1)
-				assert.ElementsMatch(cluster.ConstructsRef, []core.AnnotationKey{eu.AnnotationKey})
+				assert.Equal(cluster.ConstructsRef, core.AnnotationKeySetOf(eu.AnnotationKey))
 			},
 		},
 		{
@@ -89,7 +89,8 @@ func Test_EksClusterCreate(t *testing.T) {
 			},
 			Check: func(assert *assert.Assertions, cluster *EksCluster) {
 				assert.Equal(cluster.Name, "my-app-cluster")
-				assert.ElementsMatch(cluster.ConstructsRef, append(initialRefs, eu.AnnotationKey))
+				expect := initialRefs.CloneWith(core.AnnotationKeySetOf(eu.AnnotationKey))
+				assert.Equal(cluster.ConstructsRef, expect)
 			},
 		},
 	}
@@ -97,7 +98,7 @@ func Test_EksClusterCreate(t *testing.T) {
 		t.Run(tt.Name, func(t *testing.T) {
 			tt.Params = EksClusterCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{eu.AnnotationKey},
+				Refs:    core.AnnotationKeySetOf(eu.AnnotationKey),
 				Name:    "cluster",
 			}
 			tt.Run(t)
@@ -107,7 +108,7 @@ func Test_EksClusterCreate(t *testing.T) {
 
 func Test_EksFargateProfileCreate(t *testing.T) {
 	eu := &core.ExecutionUnit{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}}
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []coretesting.CreateCase[EksFargateProfileCreateParams, *EksFargateProfile]{
 		{
 			Name: "nil profile",
@@ -177,7 +178,7 @@ func Test_EksFargateProfileCreate(t *testing.T) {
 				assert.Equal(profile.Name, "my-app_profile_private")
 				assert.NotNil(profile.Cluster)
 				assert.Len(profile.Subnets, 2)
-				assert.ElementsMatch(profile.ConstructsRef, []core.AnnotationKey{eu.AnnotationKey})
+				assert.Equal(profile.ConstructsRef, core.AnnotationKeySetOf(eu.AnnotationKey))
 			},
 		},
 		{
@@ -191,7 +192,8 @@ func Test_EksFargateProfileCreate(t *testing.T) {
 			},
 			Check: func(assert *assert.Assertions, profile *EksFargateProfile) {
 				assert.Equal(profile.Name, "my-app_profile_private")
-				assert.ElementsMatch(profile.ConstructsRef, append(initialRefs, eu.AnnotationKey))
+				expect := initialRefs.CloneWith(core.AnnotationKeySetOf(eu.AnnotationKey))
+				assert.Equal(profile.ConstructsRef, expect)
 			},
 		},
 	}
@@ -199,7 +201,7 @@ func Test_EksFargateProfileCreate(t *testing.T) {
 		t.Run(tt.Name, func(t *testing.T) {
 			tt.Params = EksFargateProfileCreateParams{
 				AppName:     "my-app",
-				Refs:        []core.AnnotationKey{eu.AnnotationKey},
+				Refs:        core.AnnotationKeySetOf(eu.AnnotationKey),
 				Name:        "profile",
 				NetworkType: PrivateSubnet,
 				ClusterName: "cluster",
@@ -233,7 +235,7 @@ func Test_EksFargateProfileConfigure(t *testing.T) {
 
 func Test_EksNodeGroupCreate(t *testing.T) {
 	eu := &core.ExecutionUnit{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}}
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []coretesting.CreateCase[EksNodeGroupCreateParams, *EksNodeGroup]{
 		{
 			Name: "nil profile",
@@ -303,7 +305,7 @@ func Test_EksNodeGroupCreate(t *testing.T) {
 				assert.Equal(nodegroup.Name, "my-app_cluster_private_t3_medium")
 				assert.NotNil(nodegroup.Cluster)
 				assert.Len(nodegroup.Subnets, 2)
-				assert.ElementsMatch(nodegroup.ConstructsRef, []core.AnnotationKey{eu.AnnotationKey})
+				assert.Equal(nodegroup.ConstructsRef, core.AnnotationKeySetOf(eu.AnnotationKey))
 			},
 		},
 		{
@@ -317,7 +319,8 @@ func Test_EksNodeGroupCreate(t *testing.T) {
 			},
 			Check: func(assert *assert.Assertions, nodegroup *EksNodeGroup) {
 				assert.Equal(nodegroup.Name, "my-app_cluster_private_t3_medium")
-				assert.ElementsMatch(nodegroup.ConstructsRef, append(initialRefs, eu.AnnotationKey))
+				expect := initialRefs.CloneWith(core.AnnotationKeySetOf(eu.AnnotationKey))
+				assert.Equal(nodegroup.ConstructsRef, expect)
 			},
 		},
 	}
@@ -325,7 +328,7 @@ func Test_EksNodeGroupCreate(t *testing.T) {
 		t.Run(tt.Name, func(t *testing.T) {
 			tt.Params = EksNodeGroupCreateParams{
 				AppName:      "my-app",
-				Refs:         []core.AnnotationKey{eu.AnnotationKey},
+				Refs:         core.AnnotationKeySetOf(eu.AnnotationKey),
 				InstanceType: "t3.medium",
 				NetworkType:  PrivateSubnet,
 				ClusterName:  "cluster",
diff --git a/pkg/provider/aws/resources/elasticache_test.go b/pkg/provider/aws/resources/elasticache_test.go
index 695be35f..abcb866d 100644
--- a/pkg/provider/aws/resources/elasticache_test.go
+++ b/pkg/provider/aws/resources/elasticache_test.go
@@ -1,9 +1,10 @@
 package resources
 
 import (
+	"testing"
+
 	"github.com/klothoplatform/klotho/pkg/annotation"
 	"github.com/klothoplatform/klotho/pkg/core/coretesting"
-	"testing"
 
 	"github.com/klothoplatform/klotho/pkg/core"
 	"github.com/stretchr/testify/assert"
@@ -70,12 +71,12 @@ func Test_ElasticacheClusterCreate(t *testing.T) {
 			},
 			Check: func(assert *assert.Assertions, table *ElasticacheCluster) {
 				assert.Equal(table.Name, "my-app-test")
-				assert.ElementsMatch(table.ConstructsRef, []core.AnnotationKey{cluster.AnnotationKey})
+				assert.Equal(table.ConstructsRef, core.AnnotationKeySetOf(cluster.AnnotationKey))
 			},
 		},
 		{
 			Name:     "existing elasticache cluster",
-			Existing: &ElasticacheCluster{Name: "my-app-test", ConstructsRef: []core.AnnotationKey{existingKey}},
+			Existing: &ElasticacheCluster{Name: "my-app-test", ConstructsRef: core.AnnotationKeySetOf(existingKey)},
 			Want: coretesting.ResourcesExpectation{
 				Nodes: []string{
 					"aws:availability_zones:AvailabilityZones",
@@ -130,7 +131,7 @@ func Test_ElasticacheClusterCreate(t *testing.T) {
 				}},
 			Check: func(assert *assert.Assertions, table *ElasticacheCluster) {
 				assert.Equal(table.Name, "my-app-test")
-				assert.ElementsMatch(table.ConstructsRef, []core.AnnotationKey{cluster.AnnotationKey, existingKey})
+				assert.Equal(table.ConstructsRef, core.AnnotationKeySetOf(cluster.AnnotationKey, existingKey))
 			},
 		},
 	}
@@ -138,7 +139,7 @@ func Test_ElasticacheClusterCreate(t *testing.T) {
 		t.Run(tt.Name, func(t *testing.T) {
 			tt.Params = ElasticacheClusterCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{cluster.AnnotationKey},
+				Refs:    core.AnnotationKeySetOf(cluster.AnnotationKey),
 				Name:    "test",
 			}
 
diff --git a/pkg/provider/aws/resources/iam_test.go b/pkg/provider/aws/resources/iam_test.go
index 51febaa6..ba5464ca 100644
--- a/pkg/provider/aws/resources/iam_test.go
+++ b/pkg/provider/aws/resources/iam_test.go
@@ -10,7 +10,7 @@ import (
 )
 
 func Test_RoleCreate(t *testing.T) {
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name    string
 		role    *IamRole
@@ -42,7 +42,7 @@ func Test_RoleCreate(t *testing.T) {
 			metadata := RoleCreateParams{
 				AppName: "my-app",
 				Name:    "executionRole",
-				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Refs:    core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}),
 			}
 			role := &IamRole{}
 			err := role.Create(dag, metadata)
@@ -63,7 +63,7 @@ func Test_RoleCreate(t *testing.T) {
 }
 
 func Test_PolicyCreate(t *testing.T) {
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name    string
 		policy  *IamPolicy
@@ -95,7 +95,7 @@ func Test_PolicyCreate(t *testing.T) {
 			metadata := IamPolicyCreateParams{
 				AppName: "my-app",
 				Name:    "policy",
-				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Refs:    core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}),
 			}
 			policy := &IamPolicy{}
 			err := policy.Create(dag, metadata)
@@ -117,7 +117,7 @@ func Test_PolicyCreate(t *testing.T) {
 
 func Test_OidcCreate(t *testing.T) {
 	eu := &core.ExecutionUnit{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}}
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []coretesting.CreateCase[OidcCreateParams, *OpenIdConnectProvider]{
 		{
 			Name: "nil oidc",
@@ -184,7 +184,7 @@ func Test_OidcCreate(t *testing.T) {
 			Check: func(assert *assert.Assertions, oidc *OpenIdConnectProvider) {
 				assert.Equal(oidc.Name, "my-app-cluster")
 				assert.NotNil(oidc.Cluster)
-				assert.ElementsMatch(oidc.ConstructsRef, []core.AnnotationKey{eu.AnnotationKey})
+				assert.Equal(oidc.ConstructsRef, core.AnnotationKeySetOf(eu.AnnotationKey))
 			},
 		},
 		{
@@ -198,7 +198,8 @@ func Test_OidcCreate(t *testing.T) {
 			},
 			Check: func(assert *assert.Assertions, oidc *OpenIdConnectProvider) {
 				assert.Equal(oidc.Name, "my-app-cluster")
-				assert.ElementsMatch(oidc.ConstructsRef, append(initialRefs, eu.AnnotationKey))
+				expect := initialRefs.CloneWith(core.AnnotationKeySetOf(eu.AnnotationKey))
+				assert.Equal(oidc.ConstructsRef, expect)
 			},
 		},
 	}
@@ -206,7 +207,7 @@ func Test_OidcCreate(t *testing.T) {
 		t.Run(tt.Name, func(t *testing.T) {
 			tt.Params = OidcCreateParams{
 				AppName:     "my-app",
-				Refs:        []core.AnnotationKey{eu.AnnotationKey},
+				Refs:        core.AnnotationKeySetOf(eu.AnnotationKey),
 				ClusterName: "cluster",
 			}
 			tt.Run(t)
@@ -306,14 +307,14 @@ func Test_AddUnitRole(t *testing.T) {
 		{
 			name:          "Add role, none exists",
 			existingRoles: map[string]*IamRole{},
-			role:          NewIamRole("test-app", "test-role", []core.AnnotationKey{}, nil),
+			role:          NewIamRole("test-app", "test-role", core.AnnotationKeySetOf(), nil),
 		},
 		{
 			name: "Add role, one already exists",
 			existingRoles: map[string]*IamRole{
-				unitId: NewIamRole("test-app", "diff-role", []core.AnnotationKey{}, nil),
+				unitId: NewIamRole("test-app", "diff-role", core.AnnotationKeySetOf(), nil),
 			},
-			role:    NewIamRole("test-app", "test-role", []core.AnnotationKey{}, nil),
+			role:    NewIamRole("test-app", "test-role", core.AnnotationKeySetOf(), nil),
 			wantErr: true,
 		},
 	}
diff --git a/pkg/provider/aws/resources/lambda_test.go b/pkg/provider/aws/resources/lambda_test.go
index ebd76a28..751cb4cb 100644
--- a/pkg/provider/aws/resources/lambda_test.go
+++ b/pkg/provider/aws/resources/lambda_test.go
@@ -32,7 +32,7 @@ func Test_LambdaCreate(t *testing.T) {
 			},
 			Check: func(assert *assert.Assertions, lambda *LambdaFunction) {
 				assert.Equal(lambda.Name, "my-app-test")
-				assert.ElementsMatch(lambda.ConstructsRef, []core.AnnotationKey{eu.AnnotationKey})
+				assert.Equal(lambda.ConstructsRef, core.AnnotationKeySetOf(eu.AnnotationKey))
 			},
 		},
 		{
@@ -45,7 +45,7 @@ func Test_LambdaCreate(t *testing.T) {
 		t.Run(tt.Name, func(t *testing.T) {
 			tt.Params = LambdaCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{eu.AnnotationKey},
+				Refs:    core.AnnotationKeySetOf(eu.AnnotationKey),
 				Name:    eu.ID,
 			}
 
@@ -55,7 +55,7 @@ func Test_LambdaCreate(t *testing.T) {
 }
 
 func Test_LambdaPermissionCreate(t *testing.T) {
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name       string
 		permission *LambdaPermission
@@ -104,7 +104,7 @@ func Test_LambdaPermissionCreate(t *testing.T) {
 			}
 
 			metadata := LambdaPermissionCreateParams{
-				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Refs:    core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}),
 				Name:    "permission",
 				AppName: "my-app",
 			}
@@ -125,9 +125,10 @@ func Test_LambdaPermissionCreate(t *testing.T) {
 
 			assert.Equal(permission.Name, "my_app_permission")
 			if tt.permission == nil {
-				assert.ElementsMatch(permission.ConstructsRef, metadata.Refs)
+				assert.Equal(permission.ConstructsRef, metadata.Refs)
 			} else {
-				assert.ElementsMatch(permission.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				expect := initialRefs.CloneWith(core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				assert.Equal(permission.KlothoConstructRef(), expect)
 			}
 		})
 	}
diff --git a/pkg/provider/aws/resources/rds_test.go b/pkg/provider/aws/resources/rds_test.go
index e7bfc5fd..8905e06c 100644
--- a/pkg/provider/aws/resources/rds_test.go
+++ b/pkg/provider/aws/resources/rds_test.go
@@ -89,7 +89,7 @@ func Test_RdsInstanceCreate(t *testing.T) {
 
 			metadata := RdsInstanceCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{orm.AnnotationKey},
+				Refs:    core.AnnotationKeySetOf(orm.AnnotationKey),
 				Name:    orm.ID,
 			}
 			instance := &RdsInstance{}
@@ -107,14 +107,14 @@ func Test_RdsInstanceCreate(t *testing.T) {
 			instance = graphInstance.(*RdsInstance)
 
 			assert.Equal(instance.Name, "my-app-test")
-			assert.ElementsMatch(instance.ConstructsRef, []core.AnnotationKey{orm.AnnotationKey})
+			assert.Equal(instance.ConstructsRef, core.AnnotationKeySetOf(orm.AnnotationKey))
 		})
 	}
 }
 
 func Test_RdsSubnetGroupCreate(t *testing.T) {
 	orm := &core.Orm{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}}
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name        string
 		subnetGroup *RdsSubnetGroup
@@ -192,7 +192,7 @@ func Test_RdsSubnetGroupCreate(t *testing.T) {
 
 			metadata := RdsSubnetGroupCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{orm.AnnotationKey},
+				Refs:    core.AnnotationKeySetOf(orm.AnnotationKey),
 				Name:    orm.ID,
 			}
 			subnetGroup := &RdsSubnetGroup{}
@@ -209,9 +209,10 @@ func Test_RdsSubnetGroupCreate(t *testing.T) {
 			assert.Equal(subnetGroup.Name, "my-app-test")
 			if tt.subnetGroup == nil {
 				assert.Len(subnetGroup.Subnets, 2)
-				assert.ElementsMatch(subnetGroup.ConstructsRef, metadata.Refs)
+				assert.Equal(subnetGroup.ConstructsRef, metadata.Refs)
 			} else {
-				assert.ElementsMatch(subnetGroup.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}))
+				expect := initialRefs.CloneWith(core.AnnotationKeySetOf(orm.AnnotationKey))
+				assert.Equal(subnetGroup.KlothoConstructRef(), expect)
 			}
 		})
 	}
@@ -219,7 +220,7 @@ func Test_RdsSubnetGroupCreate(t *testing.T) {
 
 func Test_RdsProxyCreate(t *testing.T) {
 	orm := &core.Orm{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}}
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name  string
 		proxy *RdsProxy
@@ -302,7 +303,7 @@ func Test_RdsProxyCreate(t *testing.T) {
 
 			metadata := RdsProxyCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{orm.AnnotationKey},
+				Refs:    core.AnnotationKeySetOf(orm.AnnotationKey),
 				Name:    orm.ID,
 			}
 			proxy := &RdsProxy{}
@@ -321,9 +322,10 @@ func Test_RdsProxyCreate(t *testing.T) {
 				assert.Len(proxy.Subnets, 2)
 				assert.Len(proxy.SecurityGroups, 1)
 				assert.NotNil(proxy.Role)
-				assert.ElementsMatch(proxy.ConstructsRef, metadata.Refs)
+				assert.Equal(proxy.ConstructsRef, metadata.Refs)
 			} else {
-				assert.ElementsMatch(proxy.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}))
+				expect := initialRefs.CloneWith(core.AnnotationKeySetOf(orm.AnnotationKey))
+				assert.Equal(proxy.KlothoConstructRef(), expect)
 			}
 		})
 	}
@@ -331,7 +333,7 @@ func Test_RdsProxyCreate(t *testing.T) {
 
 func Test_RdsProxyTargetGroupCreate(t *testing.T) {
 	orm := &core.Orm{AnnotationKey: core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}}
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name        string
 		targetGroup *RdsProxyTargetGroup
@@ -369,7 +371,7 @@ func Test_RdsProxyTargetGroupCreate(t *testing.T) {
 
 			metadata := RdsProxyTargetGroupCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{orm.AnnotationKey},
+				Refs:    core.AnnotationKeySetOf(orm.AnnotationKey),
 				Name:    orm.ID,
 			}
 			targetGroup := &RdsProxyTargetGroup{}
@@ -385,9 +387,10 @@ func Test_RdsProxyTargetGroupCreate(t *testing.T) {
 
 			assert.Equal(targetGroup.Name, "my-app-test")
 			if tt.targetGroup == nil {
-				assert.ElementsMatch(targetGroup.ConstructsRef, metadata.Refs)
+				assert.Equal(targetGroup.ConstructsRef, metadata.Refs)
 			} else {
-				assert.ElementsMatch(targetGroup.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}))
+				expect := initialRefs.CloneWith(core.AnnotationKeySetOf(orm.AnnotationKey))
+				assert.Equal(targetGroup.KlothoConstructRef(), expect)
 			}
 		})
 	}
diff --git a/pkg/provider/aws/resources/s3_test.go b/pkg/provider/aws/resources/s3_test.go
index e89153e3..778bf35f 100644
--- a/pkg/provider/aws/resources/s3_test.go
+++ b/pkg/provider/aws/resources/s3_test.go
@@ -22,16 +22,16 @@ func Test_S3BucketCreate(t *testing.T) {
 			},
 			Check: func(assert *assert.Assertions, bucket *S3Bucket) {
 				assert.Equal("my-app-payloads", bucket.Name)
-				assert.ElementsMatch(bucket.ConstructsRef, []core.AnnotationKey{annotationKey})
+				assert.Equal(bucket.ConstructsRef, core.AnnotationKeySetOf(annotationKey))
 			},
 		},
 		{
 			Name: "two payloads buckets converge",
 			Existing: &S3Bucket{
 				Name: "my-app-payloads",
-				ConstructsRef: []core.AnnotationKey{core.AnnotationKey{
+				ConstructsRef: core.AnnotationKeySetOf(core.AnnotationKey{
 					ID:         "some-other-eu",
-					Capability: annotation.ExecutionUnitCapability}},
+					Capability: annotation.ExecutionUnitCapability}),
 			},
 			Want: coretesting.ResourcesExpectation{
 				Nodes: []string{
@@ -41,13 +41,13 @@ func Test_S3BucketCreate(t *testing.T) {
 			},
 			Check: func(assert *assert.Assertions, bucket *S3Bucket) {
 				assert.Equal("my-app-payloads", bucket.Name)
-				assert.ElementsMatch(bucket.ConstructsRef,
-					[]core.AnnotationKey{
+				assert.Equal(bucket.ConstructsRef,
+					core.AnnotationKeySetOf(
 						annotationKey,
 						core.AnnotationKey{
 							ID:         "some-other-eu",
 							Capability: annotation.ExecutionUnitCapability},
-					},
+					),
 				)
 			},
 		},
@@ -56,7 +56,7 @@ func Test_S3BucketCreate(t *testing.T) {
 		t.Run(tt.Name, func(t *testing.T) {
 			tt.Params = S3BucketCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{annotationKey},
+				Refs:    core.AnnotationKeySetOf(annotationKey),
 				Name:    "payloads",
 			}
 			tt.Run(t)
diff --git a/pkg/provider/aws/resources/secret_test.go b/pkg/provider/aws/resources/secret_test.go
index 146f2f08..e34fa42e 100644
--- a/pkg/provider/aws/resources/secret_test.go
+++ b/pkg/provider/aws/resources/secret_test.go
@@ -43,7 +43,7 @@ func Test_SecretCreate(t *testing.T) {
 
 			metadata := SecretCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.PersistCapability}},
+				Refs:    core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}),
 				Name:    "test",
 			}
 			secret := &Secret{}
@@ -61,7 +61,7 @@ func Test_SecretCreate(t *testing.T) {
 			secret = graphInstance.(*Secret)
 
 			assert.Equal(secret.Name, "my-app-test")
-			assert.ElementsMatch(secret.ConstructsRef, metadata.Refs)
+			assert.Equal(secret.ConstructsRef, metadata.Refs)
 		})
 	}
 }
@@ -103,7 +103,7 @@ func Test_SecretVersionCreate(t *testing.T) {
 
 			metadata := SecretVersionCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.PersistCapability}},
+				Refs:    core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.PersistCapability}),
 				Name:    "test",
 			}
 			secret := &SecretVersion{}
@@ -121,7 +121,7 @@ func Test_SecretVersionCreate(t *testing.T) {
 			secret = graphInstance.(*SecretVersion)
 
 			assert.Equal(secret.Name, "my-app-test")
-			assert.ElementsMatch(secret.ConstructsRef, metadata.Refs)
+			assert.Equal(secret.ConstructsRef, metadata.Refs)
 		})
 	}
 }
diff --git a/pkg/provider/aws/resources/securitygroup_test.go b/pkg/provider/aws/resources/securitygroup_test.go
index b2c64932..8b4faced 100644
--- a/pkg/provider/aws/resources/securitygroup_test.go
+++ b/pkg/provider/aws/resources/securitygroup_test.go
@@ -11,7 +11,7 @@ import (
 )
 
 func Test_SecurityGroupCreate(t *testing.T) {
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name string
 		sg   *SecurityGroup
@@ -52,7 +52,7 @@ func Test_SecurityGroupCreate(t *testing.T) {
 			}
 			metadata := SecurityGroupCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Refs:    core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}),
 			}
 			sg := &SecurityGroup{}
 			err := sg.Create(dag, metadata)
@@ -62,14 +62,15 @@ func Test_SecurityGroupCreate(t *testing.T) {
 			}
 			tt.want.Assert(t, dag)
 
-			graphSG := dag.GetResourceByVertexId(sg.Id().String())
+			graphSG := dag.GetResource(sg.Id())
 			sg = graphSG.(*SecurityGroup)
 
 			assert.Equal(sg.Name, "my-app")
 			if tt.sg == nil {
-				assert.ElementsMatch(sg.ConstructsRef, metadata.Refs)
+				assert.Equal(sg.ConstructsRef, metadata.Refs)
 			} else {
-				assert.ElementsMatch(sg.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				expect := initialRefs.CloneWith(metadata.Refs)
+				assert.Equal(sg.KlothoConstructRef(), expect)
 			}
 		})
 	}
diff --git a/pkg/provider/aws/resources/vpc_test.go b/pkg/provider/aws/resources/vpc_test.go
index 74ce1a04..89322de9 100644
--- a/pkg/provider/aws/resources/vpc_test.go
+++ b/pkg/provider/aws/resources/vpc_test.go
@@ -12,7 +12,7 @@ import (
 )
 
 func Test_VpcCreate(t *testing.T) {
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name string
 		vpc  *Vpc
@@ -47,7 +47,7 @@ func Test_VpcCreate(t *testing.T) {
 			}
 			metadata := VpcCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Refs:    core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}),
 			}
 
 			vpc := &Vpc{}
@@ -58,20 +58,21 @@ func Test_VpcCreate(t *testing.T) {
 			}
 			tt.want.Assert(t, dag)
 
-			graphVpc := dag.GetResourceByVertexId(vpc.Id().String())
+			graphVpc := dag.GetResource(vpc.Id())
 			vpc = graphVpc.(*Vpc)
 			assert.Equal(vpc.Name, "my_app")
 			if tt.vpc == nil {
-				assert.ElementsMatch(vpc.ConstructsRef, metadata.Refs)
+				assert.Equal(vpc.ConstructsRef, metadata.Refs)
 			} else {
-				assert.ElementsMatch(vpc.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				expect := initialRefs.CloneWith(metadata.Refs)
+				assert.Equal(vpc.KlothoConstructRef(), expect)
 			}
 		})
 	}
 }
 
 func Test_ElasticIpCreate(t *testing.T) {
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name string
 		eip  *ElasticIp
@@ -106,7 +107,7 @@ func Test_ElasticIpCreate(t *testing.T) {
 			}
 			metadata := EipCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Refs:    core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}),
 				IpName:  "ip0",
 			}
 
@@ -118,20 +119,21 @@ func Test_ElasticIpCreate(t *testing.T) {
 			}
 			tt.want.Assert(t, dag)
 
-			graphEip := dag.GetResourceByVertexId(eip.Id().String())
+			graphEip := dag.GetResource(eip.Id())
 			eip = graphEip.(*ElasticIp)
 			assert.Equal(eip.Name, "my_app_ip0")
 			if tt.eip == nil {
-				assert.ElementsMatch(eip.ConstructsRef, metadata.Refs)
+				assert.Equal(eip.ConstructsRef, metadata.Refs)
 			} else {
-				assert.ElementsMatch(eip.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				expect := initialRefs.CloneWith(metadata.Refs)
+				assert.Equal(eip.KlothoConstructRef(), expect)
 			}
 		})
 	}
 }
 
 func Test_InternetGatewayCreate(t *testing.T) {
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name string
 		igw  *InternetGateway
@@ -169,7 +171,7 @@ func Test_InternetGatewayCreate(t *testing.T) {
 			}
 			metadata := IgwCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Refs:    core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}),
 			}
 
 			igw := &InternetGateway{}
@@ -180,21 +182,22 @@ func Test_InternetGatewayCreate(t *testing.T) {
 			}
 			tt.want.Assert(t, dag)
 
-			graphIgw := dag.GetResourceByVertexId(igw.Id().String())
+			graphIgw := dag.GetResource(igw.Id())
 			igw = graphIgw.(*InternetGateway)
 			assert.Equal(igw.Name, "my_app_igw")
 			if tt.igw == nil {
 				assert.NotNil(igw.Vpc)
-				assert.ElementsMatch(igw.ConstructsRef, metadata.Refs)
+				assert.Equal(igw.ConstructsRef, metadata.Refs)
 			} else {
-				assert.ElementsMatch(igw.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				expect := initialRefs.CloneWith(metadata.Refs)
+				assert.Equal(igw.KlothoConstructRef(), expect)
 			}
 		})
 	}
 }
 
 func Test_NatGatewayCreate(t *testing.T) {
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name string
 		nat  *NatGateway
@@ -244,7 +247,7 @@ func Test_NatGatewayCreate(t *testing.T) {
 			}
 			metadata := NatCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Refs:    core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}),
 				AZ:      "0",
 			}
 
@@ -255,23 +258,24 @@ func Test_NatGatewayCreate(t *testing.T) {
 				return
 			}
 			tt.want.Assert(t, dag)
-			graphNat := dag.GetResourceByVertexId(nat.Id().String())
+			graphNat := dag.GetResource(nat.Id())
 			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)
+				assert.Equal(nat.ConstructsRef, metadata.Refs)
 			} else {
-				assert.ElementsMatch(nat.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				expect := initialRefs.CloneWith(metadata.Refs)
+				assert.Equal(nat.KlothoConstructRef(), expect)
 			}
 		})
 	}
 }
 
 func Test_SubnetCreate(t *testing.T) {
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name     string
 		subnet   *Subnet
@@ -421,7 +425,7 @@ func Test_SubnetCreate(t *testing.T) {
 			}
 			metadata := SubnetCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Refs:    core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}),
 				AZ:      tt.subnet.AvailabilityZone.Property,
 				Type:    tt.subnet.Type,
 			}
@@ -437,23 +441,24 @@ func Test_SubnetCreate(t *testing.T) {
 			}
 			tt.want.Assert(t, dag)
 
-			graphSubnet := dag.GetResourceByVertexId(subnet.Id().String())
+			graphSubnet := dag.GetResource(subnet.Id())
 			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)
+				assert.Equal(subnet.ConstructsRef, metadata.Refs)
 			} else {
-				assert.ElementsMatch(subnet.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				expect := initialRefs.CloneWith(metadata.Refs)
+				assert.Equal(subnet.KlothoConstructRef(), expect)
 			}
 		})
 	}
 }
 
 func Test_RouteTableCreate(t *testing.T) {
-	initialRefs := []core.AnnotationKey{{ID: "first"}}
+	initialRefs := core.AnnotationKeySetOf(core.AnnotationKey{ID: "first"})
 	cases := []struct {
 		name string
 		rt   *RouteTable
@@ -494,7 +499,7 @@ func Test_RouteTableCreate(t *testing.T) {
 			}
 			metadata := RouteTableCreateParams{
 				AppName: "my-app",
-				Refs:    []core.AnnotationKey{{ID: "test", Capability: annotation.ExecutionUnitCapability}},
+				Refs:    core.AnnotationKeySetOf(core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}),
 				Name:    "private0",
 			}
 
@@ -506,15 +511,16 @@ func Test_RouteTableCreate(t *testing.T) {
 			}
 			tt.want.Assert(t, dag)
 
-			graphRT := dag.GetResourceByVertexId(rt.Id().String())
+			graphRT := dag.GetResource(rt.Id())
 			rt = graphRT.(*RouteTable)
 
 			assert.Equal(rt.Name, "my_app_private0")
 			if tt.rt == nil {
 				assert.NotNil(rt.Vpc)
-				assert.ElementsMatch(rt.ConstructsRef, metadata.Refs)
+				assert.Equal(rt.ConstructsRef, metadata.Refs)
 			} else {
-				assert.ElementsMatch(rt.KlothoConstructRef(), append(initialRefs, core.AnnotationKey{ID: "test", Capability: annotation.ExecutionUnitCapability}))
+				expect := initialRefs.CloneWith(metadata.Refs)
+				assert.Equal(rt.KlothoConstructRef(), expect)
 			}
 		})
 	}
diff --git a/pkg/provider/aws/secret_test.go b/pkg/provider/aws/secret_test.go
index 96638ae3..fa98fd67 100644
--- a/pkg/provider/aws/secret_test.go
+++ b/pkg/provider/aws/secret_test.go
@@ -1,7 +1,6 @@
 package aws
 
 import (
-	"fmt"
 	"testing"
 
 	"github.com/klothoplatform/klotho/pkg/annotation"
@@ -25,8 +24,8 @@ func TestGenerateSecretsResources(t *testing.T) {
 		secrets             []string
 		execUnit            *core.ExecutionUnit
 		want                coretesting.ResourcesExpectation
-		wantManagedPolicies func(secretResolver func(string) *resources.Secret) []resources.StatementEntry
-		wantInlinePolicies  func(secretResolver func(string) *resources.Secret) []resources.StatementEntry
+		wantManagedPolicies func(secretResolver func(core.ResourceId) *resources.Secret) []resources.StatementEntry
+		wantInlinePolicies  func(secretResolver func(core.ResourceId) *resources.Secret) []resources.StatementEntry
 	}{
 		{
 			name:     "two secrets",
@@ -44,10 +43,12 @@ func TestGenerateSecretsResources(t *testing.T) {
 					{Source: "aws:secret_version:AppName-secret2", Destination: "aws:secret:AppName-secret2"},
 				},
 			},
-			wantManagedPolicies: func(secretResolver func(string) *resources.Secret) []resources.StatementEntry { return nil },
-			wantInlinePolicies: func(secretResolver func(string) *resources.Secret) []resources.StatementEntry {
-				secret1 := secretResolver(fmt.Sprintf(`aws:secret:%s-secret1`, AppName))
-				secret2 := secretResolver(fmt.Sprintf(`aws:secret:%s-secret2`, AppName))
+			wantManagedPolicies: func(secretResolver func(core.ResourceId) *resources.Secret) []resources.StatementEntry { return nil },
+			wantInlinePolicies: func(secretResolver func(core.ResourceId) *resources.Secret) []resources.StatementEntry {
+				secret1 := &resources.Secret{Name: AppName + "-secret1"}
+				secret1 = secretResolver(secret1.Id())
+				secret2 := &resources.Secret{Name: AppName + "-secret2"}
+				secret2 = secretResolver(secret2.Id())
 				return []resources.StatementEntry{
 					{
 						Effect: "Allow",
@@ -76,8 +77,8 @@ func TestGenerateSecretsResources(t *testing.T) {
 			name:                "no secrets",
 			execUnit:            execUnit,
 			want:                coretesting.ResourcesExpectation{},
-			wantManagedPolicies: func(secretResolver func(string) *resources.Secret) []resources.StatementEntry { return nil },
-			wantInlinePolicies:  func(secretResolver func(string) *resources.Secret) []resources.StatementEntry { return nil },
+			wantManagedPolicies: func(secretResolver func(core.ResourceId) *resources.Secret) []resources.StatementEntry { return nil },
+			wantInlinePolicies:  func(secretResolver func(core.ResourceId) *resources.Secret) []resources.StatementEntry { return nil },
 		},
 	}
 	for _, tt := range cases {
@@ -109,8 +110,8 @@ func TestGenerateSecretsResources(t *testing.T) {
 
 			tt.want.Assert(t, dag)
 
-			wantManagedPolicies := tt.wantManagedPolicies(func(secretId string) *resources.Secret {
-				resource := dag.GetResourceByVertexId(secretId)
+			wantManagedPolicies := tt.wantManagedPolicies(func(secretId core.ResourceId) *resources.Secret {
+				resource := dag.GetResource(secretId)
 				assert.NotNil(resource)
 				if secret, foundSecret := resource.(*resources.Secret); foundSecret {
 					return secret
@@ -120,8 +121,8 @@ func TestGenerateSecretsResources(t *testing.T) {
 				return nil
 			})
 
-			wantInlinePolicies := tt.wantInlinePolicies(func(secretId string) *resources.Secret {
-				resource := dag.GetResourceByVertexId(secretId)
+			wantInlinePolicies := tt.wantInlinePolicies(func(secretId core.ResourceId) *resources.Secret {
+				resource := dag.GetResource(secretId)
 				assert.NotNil(resource)
 				if secret, foundSecret := resource.(*resources.Secret); foundSecret {
 					return secret