Repository: klothoplatform/klotho
Pull request #856 (2024-01-10 18:52:00)

Calculate edges for parent icons
Fixes cloudfront distribution -> rest_api and other cases where a parent has edges


Patch:
diff --git a/pkg/engine2/visualizer.go b/pkg/engine2/visualizer.go
index c09dc861..7f21f968 100644
--- a/pkg/engine2/visualizer.go
+++ b/pkg/engine2/visualizer.go
@@ -92,7 +92,7 @@ func (e *Engine) GetViewsDag(view View, sol solution_context.SolutionContext) (v
 	for _, id := range ids {
 		var err error
 		switch tag := GetResourceVizTag(e.Kb, view, id); tag {
-		case NoRenderTag:
+		case NoRenderTag, SmallIconTag:
 			continue
 		case ParentIconTag, BigIconTag:
 			err = viewDag.AddVertex(&visualizer.VisResource{
@@ -100,6 +100,8 @@ func (e *Engine) GetViewsDag(view View, sol solution_context.SolutionContext) (v
 				Children: make(set.Set[construct.ResourceId]),
 				Tag:      string(tag),
 			})
+		default:
+			errs = errors.Join(errs, fmt.Errorf("unknown tag %s", tag))
 		}
 		errs = errors.Join(errs, err)
 	}
@@ -107,74 +109,50 @@ func (e *Engine) GetViewsDag(view View, sol solution_context.SolutionContext) (v
 		return nil, errs
 	}
 
-	// Second pass sets up the small icons & edges between big icons
+	// Second pass sets up the small icons & parents
 	for _, id := range ids {
 		switch tag := GetResourceVizTag(e.Kb, view, id); tag {
-		case NoRenderTag:
-			continue
-		case ParentIconTag:
-			err := e.handleParentIcon(sol, view, viewDag, id)
+		case ParentIconTag, BigIconTag:
+			err := e.setupAncestry(sol, view, viewDag, id)
 			if err != nil {
-				errs = errors.Join(errs, fmt.Errorf("failed to handle parent icon %s: %w", id, err))
+				errs = errors.Join(errs, fmt.Errorf("failed to handle %s icon %s: %w", tag, id, err))
 			}
-		case BigIconTag:
-			err := e.handleBigIcon(sol, view, undirected, viewDag, id)
+		}
+	}
+
+	// Third pass for the edges. Needs to happen after parents to exclude edges to a node's ancestry
+	for _, id := range ids {
+		switch tag := GetResourceVizTag(e.Kb, view, id); tag {
+		case ParentIconTag, BigIconTag:
+			err := e.makeEdges(sol, view, viewDag, id)
 			if err != nil {
-				errs = errors.Join(errs, fmt.Errorf("failed to handle big icon %s: %w", id, err))
+				errs = errors.Join(errs, fmt.Errorf("failed to handle %s icon %s: %w", tag, id, err))
 			}
-		case SmallIconTag:
-			// Small icons don't need special handling, handleBigIcon will look for any relevant small icons to include
-		default:
-			errs = errors.Join(errs, fmt.Errorf("unknown tag %s", tag))
 		}
 	}
 
 	return viewDag, errs
 }
 
-func (e *Engine) handleParentIcon(
-	sol solution_context.SolutionContext,
-	view View,
-	viewDag visualizer.VisGraph,
-	id construct.ResourceId,
-) error {
-	this, err := viewDag.Vertex(id)
-	if err != nil {
-		return err
-	}
-
-	parent, err := e.findParent(view, sol, viewDag, id)
-	if err != nil {
-		return err
-	}
-	this.Parent = parent
-
-	if err := e.setChildren(sol, view, this); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// handleBigIcon sets the parent of the big icon if there is a group it should be added to and
-// adds edges to any other big icons based on having the proper connections (network & permissions).
-func (e *Engine) handleBigIcon(
+func (e *Engine) makeEdges(
 	sol solution_context.SolutionContext,
 	view View,
-	undirected construct.Graph,
 	viewDag visualizer.VisGraph,
 	id construct.ResourceId,
-) error {
-	this, err := viewDag.Vertex(id)
-	if err != nil {
-		return err
+) (err error) {
+	ancestors := make(set.Set[construct.ResourceId])
+	for ancestor := id; !ancestor.IsZero() && err == nil; {
+		ancestors.Add(ancestor)
+
+		var ancestorVert *visualizer.VisResource
+		ancestorVert, err = viewDag.Vertex(ancestor)
+		if ancestorVert != nil {
+			ancestor = ancestorVert.Parent
+		}
 	}
-
-	parent, err := e.findParent(view, sol, viewDag, id)
 	if err != nil {
 		return err
 	}
-	this.Parent = parent
 
 	targets, err := construct.TopologicalSort(viewDag)
 	if err != nil {
@@ -183,10 +161,16 @@ func (e *Engine) handleBigIcon(
 
 	var errs error
 	for _, target := range targets {
-		if target == id {
+		if ancestors.Contains(target) {
+			// Don't draw edges from a node to its ancestors, since it already lives inside of them
 			continue
 		}
-		if tag := GetResourceVizTag(e.Kb, view, target); tag != BigIconTag {
+
+		_, targetErr := viewDag.Vertex(target)
+		if errors.Is(targetErr, graph.ErrVertexNotFound) {
+			continue
+		} else if targetErr != nil {
+			errs = errors.Join(errs, targetErr)
 			continue
 		}
 
@@ -207,9 +191,27 @@ func (e *Engine) handleBigIcon(
 			})))
 		}
 	}
-	if errs != nil {
-		return errs
+	return errs
+}
+
+// setupAncestry sets the parent of the big icon if there is a group it should be added to and
+// adds edges to any other big icons based on having the proper connections (network & permissions).
+func (e *Engine) setupAncestry(
+	sol solution_context.SolutionContext,
+	view View,
+	viewDag visualizer.VisGraph,
+	id construct.ResourceId,
+) error {
+	this, err := viewDag.Vertex(id)
+	if err != nil {
+		return err
+	}
+
+	parent, err := e.findParent(view, sol, viewDag, id)
+	if err != nil {
+		return err
 	}
+	this.Parent = parent
 
 	if err := e.setChildren(sol, view, this); err != nil {
 		return err


Test patch:
diff --git a/pkg/engine2/testdata/2_routes.dataflow-viz.yaml b/pkg/engine2/testdata/2_routes.dataflow-viz.yaml
index c36389dc..60abd522 100755
--- a/pkg/engine2/testdata/2_routes.dataflow-viz.yaml
+++ b/pkg/engine2/testdata/2_routes.dataflow-viz.yaml
@@ -1,5 +1,12 @@
 provider: aws
 resources:
+  lambda_function/lambda_function_0:
+    children:
+        - aws:ecr_image:lambda_function_0-image
+        - aws:ecr_repo:ecr_repo-0
+        - aws:iam_role:lambda_function_0-ExecutionRole
+    tag: big
+
   lambda_function/lambda_function_1:
     children:
         - aws:ecr_image:lambda_function_1-image
@@ -7,13 +14,22 @@ resources:
         - aws:iam_role:lambda_function_1-ExecutionRole
     tag: big
 
-  lambda_function/lambda_function_0:
-    children:
-        - aws:ecr_image:lambda_function_0-image
-        - aws:ecr_repo:ecr_repo-0
-        - aws:iam_role:lambda_function_0-ExecutionRole
+  aws:api_integration:rest_api_1/integ0:
+    parent: rest_api/rest_api_1
+    tag: big
+
+  aws:api_integration:rest_api_1/integ0 -> lambda_function/lambda_function_0:
+    path:
+        - aws:lambda_permission:integ0-lambda_function_0
+
+  aws:api_integration:rest_api_1/integ1:
+    parent: rest_api/rest_api_1
     tag: big
 
+  aws:api_integration:rest_api_1/integ1 -> lambda_function/lambda_function_1:
+    path:
+        - aws:lambda_permission:integ1-lambda_function_1
+
   rest_api/rest_api_1:
     children:
         - aws:api_deployment:rest_api_1:api_deployment-0
@@ -28,19 +44,15 @@ resources:
         - aws:api_stage:rest_api_1:api_stage-0
     tag: parent
 
-  aws:api_integration:rest_api_1/integ1:
-    parent: rest_api/rest_api_1
-    tag: big
-
-  aws:api_integration:rest_api_1/integ1 -> lambda_function/lambda_function_1:
+  rest_api/rest_api_1 -> aws:api_integration:rest_api_1/integ0:
     path:
-        - aws:lambda_permission:integ1-lambda_function_1
-
-  aws:api_integration:rest_api_1/integ0:
-    parent: rest_api/rest_api_1
-    tag: big
+        - aws:api_method:rest_api_1:integ0-api_method
+        - aws:api_resource:rest_api_1:api_resource-0
+        - aws:api_resource:rest_api_1:lambda0
 
-  aws:api_integration:rest_api_1/integ0 -> lambda_function/lambda_function_0:
+  rest_api/rest_api_1 -> aws:api_integration:rest_api_1/integ1:
     path:
-        - aws:lambda_permission:integ0-lambda_function_0
+        - aws:api_method:rest_api_1:integ1-api_method
+        - aws:api_resource:rest_api_1:api_resource-1
+        - aws:api_resource:rest_api_1:lambda1
 
diff --git a/pkg/engine2/testdata/cf_distribution.dataflow-viz.yaml b/pkg/engine2/testdata/cf_distribution.dataflow-viz.yaml
new file mode 100755
index 00000000..74c3230a
--- /dev/null
+++ b/pkg/engine2/testdata/cf_distribution.dataflow-viz.yaml
@@ -0,0 +1,24 @@
+provider: aws
+resources:
+  rest_api/rest_api_1:
+    children:
+        - aws:api_deployment:rest_api_1:api_deployment-0
+        - aws:api_stage:rest_api_1:cloudfront_distribution_2-rest_api_1
+    tag: parent
+
+  s3_bucket/s3-bucket-3:
+    tag: big
+
+  cloudfront_distribution/cloudfront_distribution_2:
+    tag: big
+
+  cloudfront_distribution/cloudfront_distribution_2 -> rest_api/rest_api_1:
+    path:
+        - aws:api_deployment:rest_api_1:api_deployment-0
+        - aws:api_stage:rest_api_1:cloudfront_distribution_2-rest_api_1
+
+  cloudfront_distribution/cloudfront_distribution_2 -> s3_bucket/s3-bucket-3:
+    path:
+        - aws:cloudfront_origin_access_identity:cloudfront_origin_access_identity-0
+        - aws:s3_bucket_policy:s3_bucket_policy-0
+
diff --git a/pkg/engine2/testdata/cf_distribution.expect.yaml b/pkg/engine2/testdata/cf_distribution.expect.yaml
new file mode 100755
index 00000000..5dcba5de
--- /dev/null
+++ b/pkg/engine2/testdata/cf_distribution.expect.yaml
@@ -0,0 +1,83 @@
+resources:
+    aws:cloudfront_distribution:cloudfront_distribution_2:
+        CloudfrontDefaultCertificate: true
+        DefaultCacheBehavior:
+            AllowedMethods:
+                - DELETE
+                - GET
+                - HEAD
+                - OPTIONS
+                - PATCH
+                - POST
+                - PUT
+            CachedMethods:
+                - HEAD
+                - GET
+            DefaultTtl: 3600
+            ForwardedValues:
+                Cookies:
+                    Forward: none
+                QueryString: true
+            MaxTtl: 86400
+            MinTtl: 0
+            TargetOriginId: s3-bucket-3
+            ViewerProtocolPolicy: allow-all
+        Enabled: true
+        Origins:
+            - DomainName: aws:s3_bucket:s3-bucket-3#BucketRegionalDomainName
+              OriginId: s3-bucket-3
+              S3OriginConfig:
+                OriginAccessIdentity: aws:cloudfront_origin_access_identity:cloudfront_origin_access_identity-0#CloudfrontAccessIdentityPath
+            - CustomOriginConfig:
+                HttpPort: 80
+                HttpsPort: 443
+                OriginProtocolPolicy: https-only
+                OriginSslProtocols:
+                    - TLSv1.2
+                    - TLSv1
+                    - SSLv3
+                    - TLSv1.1
+              DomainName: aws:api_stage:rest_api_1:cloudfront_distribution_2-rest_api_1#StageInvokeUrl
+              OriginId: cloudfront_distribution_2-rest_api_1
+              OriginPath: /stage
+        Restrictions:
+            GeoRestriction:
+                RestrictionType: none
+    aws:api_stage:rest_api_1:cloudfront_distribution_2-rest_api_1:
+        Deployment: aws:api_deployment:rest_api_1:api_deployment-0
+        RestApi: aws:rest_api:rest_api_1
+        StageName: stage
+    aws:cloudfront_origin_access_identity:cloudfront_origin_access_identity-0:
+        Comment: this is needed to set up S3 polices so that the S3 bucket is not public
+    aws:api_deployment:rest_api_1:api_deployment-0:
+        RestApi: aws:rest_api:rest_api_1
+    aws:s3_bucket_policy:s3_bucket_policy-0:
+        Bucket: aws:s3_bucket:s3-bucket-3
+        Policy:
+            Statement:
+                - Action:
+                    - s3:GetObject
+                  Effect: Allow
+                  Principal:
+                    AWS:
+                        - aws:cloudfront_origin_access_identity:cloudfront_origin_access_identity-0#IamArn
+                  Resource:
+                    - aws:s3_bucket:s3-bucket-3#AllBucketDirectory
+            Version: "2012-10-17"
+    aws:rest_api:rest_api_1:
+        BinaryMediaTypes:
+            - application/octet-stream
+            - image/*
+    aws:s3_bucket:s3-bucket-3:
+        ForceDestroy: true
+        SSEAlgorithm: AES256
+edges:
+    aws:cloudfront_distribution:cloudfront_distribution_2 -> aws:api_stage:rest_api_1:cloudfront_distribution_2-rest_api_1:
+    ? aws:cloudfront_distribution:cloudfront_distribution_2 -> aws:cloudfront_origin_access_identity:cloudfront_origin_access_identity-0
+    :
+    aws:cloudfront_distribution:cloudfront_distribution_2 -> aws:s3_bucket:s3-bucket-3:
+    aws:api_stage:rest_api_1:cloudfront_distribution_2-rest_api_1 -> aws:api_deployment:rest_api_1:api_deployment-0:
+    aws:api_stage:rest_api_1:cloudfront_distribution_2-rest_api_1 -> aws:rest_api:rest_api_1:
+    aws:cloudfront_origin_access_identity:cloudfront_origin_access_identity-0 -> aws:s3_bucket_policy:s3_bucket_policy-0:
+    aws:api_deployment:rest_api_1:api_deployment-0 -> aws:rest_api:rest_api_1:
+    aws:s3_bucket_policy:s3_bucket_policy-0 -> aws:s3_bucket:s3-bucket-3:
diff --git a/pkg/engine2/testdata/cf_distribution.iac-viz.yaml b/pkg/engine2/testdata/cf_distribution.iac-viz.yaml
new file mode 100755
index 00000000..422ae6d4
--- /dev/null
+++ b/pkg/engine2/testdata/cf_distribution.iac-viz.yaml
@@ -0,0 +1,24 @@
+provider: aws
+resources:
+  rest_api/rest_api_1:
+
+  aws:api_deployment:rest_api_1/api_deployment-0:
+
+  aws:api_deployment:rest_api_1/api_deployment-0 -> rest_api/rest_api_1:
+  cloudfront_origin_access_identity/cloudfront_origin_access_identity-0:
+
+  s3_bucket/s3-bucket-3:
+
+  aws:api_stage:rest_api_1/cloudfront_distribution_2-rest_api_1:
+
+  aws:api_stage:rest_api_1/cloudfront_distribution_2-rest_api_1 -> aws:api_deployment:rest_api_1/api_deployment-0:
+  aws:api_stage:rest_api_1/cloudfront_distribution_2-rest_api_1 -> rest_api/rest_api_1:
+  s3_bucket_policy/s3_bucket_policy-0:
+
+  s3_bucket_policy/s3_bucket_policy-0 -> cloudfront_origin_access_identity/cloudfront_origin_access_identity-0:
+  s3_bucket_policy/s3_bucket_policy-0 -> s3_bucket/s3-bucket-3:
+  cloudfront_distribution/cloudfront_distribution_2:
+
+  cloudfront_distribution/cloudfront_distribution_2 -> aws:api_stage:rest_api_1/cloudfront_distribution_2-rest_api_1:
+  cloudfront_distribution/cloudfront_distribution_2 -> cloudfront_origin_access_identity/cloudfront_origin_access_identity-0:
+  cloudfront_distribution/cloudfront_distribution_2 -> s3_bucket/s3-bucket-3:
diff --git a/pkg/engine2/testdata/cf_distribution.input.yaml b/pkg/engine2/testdata/cf_distribution.input.yaml
new file mode 100644
index 00000000..7796585a
--- /dev/null
+++ b/pkg/engine2/testdata/cf_distribution.input.yaml
@@ -0,0 +1,20 @@
+constraints:
+  - node: aws:cloudfront_distribution:cloudfront_distribution_2
+    operator: add
+    scope: application
+  - node: aws:rest_api:rest_api_1
+    operator: add
+    scope: application
+  - node: aws:s3_bucket:s3-bucket-3
+    operator: add
+    scope: application
+  - operator: must_exist
+    scope: edge
+    target:
+      source: aws:cloudfront_distribution:cloudfront_distribution_2
+      target: aws:rest_api:rest_api_1
+  - operator: must_exist
+    scope: edge
+    target:
+      source: aws:cloudfront_distribution:cloudfront_distribution_2
+      target: aws:s3_bucket:s3-bucket-3
diff --git a/pkg/engine2/testdata/delete_resource_and_iacdeps.dataflow-viz.yaml b/pkg/engine2/testdata/delete_resource_and_iacdeps.dataflow-viz.yaml
index c3a3924f..731a2dbf 100644
--- a/pkg/engine2/testdata/delete_resource_and_iacdeps.dataflow-viz.yaml
+++ b/pkg/engine2/testdata/delete_resource_and_iacdeps.dataflow-viz.yaml
@@ -1,5 +1,9 @@
 provider: aws
 resources:
+  aws:api_integration:rest_api_0/rest_api_0_integration_0:
+    parent: rest_api/rest_api_0
+    tag: big
+
   rest_api/rest_api_0:
     children:
         - aws:api_deployment:rest_api_0:api_deployment-0
@@ -9,7 +13,8 @@ resources:
         - aws:api_stage:rest_api_0:api_stage-0
     tag: parent
 
-  aws:api_integration:rest_api_0/rest_api_0_integration_0:
-    parent: rest_api/rest_api_0
-    tag: big
+  rest_api/rest_api_0 -> aws:api_integration:rest_api_0/rest_api_0_integration_0:
+    path:
+        - aws:api_method:rest_api_0:rest_api_0_integration_0_method
+        - aws:api_resource:rest_api_0:api_resource-0
 
diff --git a/pkg/engine2/testdata/ecs_rds.expect.yaml b/pkg/engine2/testdata/ecs_rds.expect.yaml
index 674b9038..7a20d6dc 100755
--- a/pkg/engine2/testdata/ecs_rds.expect.yaml
+++ b/pkg/engine2/testdata/ecs_rds.expect.yaml
@@ -169,6 +169,11 @@ resources:
               Protocol: "-1"
               ToPort: 0
         IngressRules:
+            - Description: Allow ingress traffic from within the same security group
+              FromPort: 0
+              Protocol: "-1"
+              Self: true
+              ToPort: 0
             - CidrBlocks:
                 - 10.0.128.0/18
               Description: Allow ingress traffic from ip addresses within the subnet subnet-0
@@ -181,11 +186,6 @@ resources:
               FromPort: 0
               Protocol: "-1"
               ToPort: 0
-            - Description: Allow ingress traffic from within the same security group
-              FromPort: 0
-              Protocol: "-1"
-              Self: true
-              ToPort: 0
         Vpc: aws:vpc:vpc-0
     aws:route_table:vpc-0:subnet-0-route_table:
         Routes:
diff --git a/pkg/engine2/testdata/extend_graph.dataflow-viz.yaml b/pkg/engine2/testdata/extend_graph.dataflow-viz.yaml
index a43ab65d..a93075e5 100755
--- a/pkg/engine2/testdata/extend_graph.dataflow-viz.yaml
+++ b/pkg/engine2/testdata/extend_graph.dataflow-viz.yaml
@@ -7,6 +7,14 @@ resources:
         - aws:iam_role:lambda_function_0-ExecutionRole
     tag: big
 
+  aws:api_integration:rest_api_1/rest_api_1_integration_0:
+    parent: rest_api/rest_api_1
+    tag: big
+
+  aws:api_integration:rest_api_1/rest_api_1_integration_0 -> lambda_function/lambda_function_0:
+    path:
+        - aws:lambda_permission:rest_api_1_integration_0_lambda_function_0
+
   rest_api/rest_api_1:
     children:
         - aws:api_deployment:rest_api_1:api_deployment-0
@@ -16,14 +24,11 @@ resources:
         - aws:api_stage:rest_api_1:api_stage-0
     tag: parent
 
-  dynamodb_table/dynamodb_table_3:
-    tag: big
+  rest_api/rest_api_1 -> aws:api_integration:rest_api_1/rest_api_1_integration_0:
+    path:
+        - aws:api_method:rest_api_1:rest_api_1_integration_0_method
+        - aws:api_resource:rest_api_1:api_resource-0
 
-  aws:api_integration:rest_api_1/rest_api_1_integration_0:
-    parent: rest_api/rest_api_1
+  dynamodb_table/dynamodb_table_3:
     tag: big
 
-  aws:api_integration:rest_api_1/rest_api_1_integration_0 -> lambda_function/lambda_function_0:
-    path:
-        - aws:lambda_permission:rest_api_1_integration_0_lambda_function_0
-
diff --git a/pkg/engine2/testdata/k8s_api.dataflow-viz.yaml b/pkg/engine2/testdata/k8s_api.dataflow-viz.yaml
index f1792347..f4a7ddad 100755
--- a/pkg/engine2/testdata/k8s_api.dataflow-viz.yaml
+++ b/pkg/engine2/testdata/k8s_api.dataflow-viz.yaml
@@ -18,6 +18,14 @@ resources:
         - kubernetes:service:eks_cluster-0:restapi4integration0-pod2
         - kubernetes:target_group_binding:eks_cluster-0:restapi4integration0-pod2
 
+  aws:api_integration:rest_api_4/rest_api_4_integration_0:
+    parent: rest_api/rest_api_4
+    tag: big
+
+  aws:api_integration:rest_api_4/rest_api_4_integration_0 -> load_balancer/rest-api-4-integbcc77100:
+    path:
+        - aws:vpc_link:rest_api_4_integration_0-pod2
+
   kubernetes:helm_chart:eks_cluster-0/metricsserver:
     children:
         - aws:iam_role:ClusterRole-eks_cluster-0
@@ -53,6 +61,11 @@ resources:
         - aws:api_stage:rest_api_4:api_stage-0
     tag: parent
 
+  rest_api/rest_api_4 -> aws:api_integration:rest_api_4/rest_api_4_integration_0:
+    path:
+        - aws:api_method:rest_api_4:rest_api_4_integration_0_method
+        - aws:api_resource:rest_api_4:api_resource-0
+
   eks_cluster/eks_cluster-0:
     children:
         - aws:iam_role:ClusterRole-eks_cluster-0
@@ -66,11 +79,3 @@ resources:
     parent: vpc/vpc-0
     tag: parent
 
-  aws:api_integration:rest_api_4/rest_api_4_integration_0:
-    parent: rest_api/rest_api_4
-    tag: big
-
-  aws:api_integration:rest_api_4/rest_api_4_integration_0 -> load_balancer/rest-api-4-integbcc77100:
-    path:
-        - aws:vpc_link:rest_api_4_integration_0-pod2
-
diff --git a/pkg/engine2/testdata/k8s_api.expect.yaml b/pkg/engine2/testdata/k8s_api.expect.yaml
index dff52939..79ca9474 100755
--- a/pkg/engine2/testdata/k8s_api.expect.yaml
+++ b/pkg/engine2/testdata/k8s_api.expect.yaml
@@ -267,12 +267,12 @@ resources:
                         - ec2.amazonaws.com
             Version: "2012-10-17"
         ManagedPolicies:
-            - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
-            - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
             - arn:aws:iam::aws:policy/AWSCloudMapFullAccess
             - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy
             - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
             - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
+            - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
+            - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
     aws:iam_role:pod2:
         AssumeRolePolicyDoc:
             Statement:
@@ -484,6 +484,12 @@ resources:
               Protocol: "-1"
               ToPort: 0
         IngressRules:
+            - CidrBlocks:
+                - 0.0.0.0/0
+              Description: Allows ingress traffic from the EKS control plane
+              FromPort: 9443
+              Protocol: TCP
+              ToPort: 9443
             - Description: Allow ingress traffic from within the same security group
               FromPort: 0
               Protocol: "-1"
@@ -501,12 +507,6 @@ resources:
               FromPort: 0
               Protocol: "-1"
               ToPort: 0
-            - CidrBlocks:
-                - 0.0.0.0/0
-              Description: Allows ingress traffic from the EKS control plane
-              FromPort: 9443
-              Protocol: TCP
-              ToPort: 9443
         Vpc: aws:vpc:vpc-0
     aws:route_table:vpc-0:subnet-0-route_table:
         Routes:
diff --git a/pkg/engine2/testdata/lambda_efs.expect.yaml b/pkg/engine2/testdata/lambda_efs.expect.yaml
index dba34bea..01220380 100755
--- a/pkg/engine2/testdata/lambda_efs.expect.yaml
+++ b/pkg/engine2/testdata/lambda_efs.expect.yaml
@@ -62,8 +62,8 @@ resources:
                         - aws:efs_file_system:test-efs-fs#Arn
                 Version: "2012-10-17"
         ManagedPolicies:
-            - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
             - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
+            - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
     aws:ecr_repo:ecr_repo-0:
         ForceDelete: true
     aws:efs_mount_target:test-efs-fs:lambda_test_app-test-efs-fs:
diff --git a/pkg/engine2/testdata/vpc_import_to_lambda.expect.yaml b/pkg/engine2/testdata/vpc_import_to_lambda.expect.yaml
index 9181f1ad..32b7551f 100644
--- a/pkg/engine2/testdata/vpc_import_to_lambda.expect.yaml
+++ b/pkg/engine2/testdata/vpc_import_to_lambda.expect.yaml
@@ -48,8 +48,8 @@ resources:
                         - lambda.amazonaws.com
             Version: "2012-10-17"
         ManagedPolicies:
-            - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
             - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
+            - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
     aws:subnet:subnet2:
         Type: private
         Vpc: aws:vpc:vpc