Repository: keikoproj/instance-manager
Pull request #55 (2019-10-03 07:27:33)

updating with managed policies logic
This addresses https://github.com/keikoproj/instance-manager/issues/54 

There is a slight change in implementation as mentioned in the issue, This proposed solution expects user to provide ManagedPolicyName instead of Managed Policy ARN to avoid any syntax issues in ARN. This solution takes the names and construct the ARNs.  

codecov[bot]: # [Codecov](https://codecov.io/gh/keikoproj/instance-manager/pull/55?src=pr&el=h1) Report
> :exclamation: No coverage uploaded for pull request base (`master@6273621`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference#section-missing-base-commit).
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/keikoproj/instance-manager/pull/55/graphs/tree.svg?width=650&token=IJbjmSBliL&height=150&src=pr)](https://codecov.io/gh/keikoproj/instance-manager/pull/55?src=pr&el=tree)

```diff
@@            Coverage Diff            @@
##             master      #55   +/-   ##
=========================================
  Coverage          ?   72.12%           
=========================================
  Files             ?        6           
  Lines             ?      782           
  Branches          ?        0           
=========================================
  Hits              ?      564           
  Misses            ?      160           
  Partials          ?       58
```


| [Impacted Files](https://codecov.io/gh/keikoproj/instance-manager/pull/55?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [...rovisioners/ekscloudformation/ekscloudformation.go](https://codecov.io/gh/keikoproj/instance-manager/pull/55/diff?src=pr&el=tree#diff-Y29udHJvbGxlcnMvcHJvdmlzaW9uZXJzL2Vrc2Nsb3VkZm9ybWF0aW9uL2Vrc2Nsb3VkZm9ybWF0aW9uLmdv) | `80.9% <100%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/keikoproj/instance-manager/pull/55?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/keikoproj/instance-manager/pull/55?src=pr&el=footer). Last update [6273621...f60cec3](https://codecov.io/gh/keikoproj/instance-manager/pull/55?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

mnkg561: [Work in Progress] -- Implementation is done but was unable to test it from locally as probably code is looking for metadata service to get the region as part of the bootstrap since local machine doesn't have it, it is throwing error.

```
MTV:instance-manager nmogulla$ make run
/Users/nmogulla/Desktop/Eclipse_Workspace/GoProjects2/bin/controller-gen object:headerFile=./hack/boilerplate.go.txt paths=./api/...
go fmt ./...
go vet ./...
go run ./main.go
INFO[2019-10-03T00:14:11-07:00] Go Version: go1.13.1                         
INFO[2019-10-03T00:14:11-07:00] Go OS/Arch: darwin/amd64                     
INFO[2019-10-03T00:14:11-07:00] Controller Version: instancemgr-0.3.2        
INFO[2019-10-03T00:14:11-07:00]                                              
FATA[2019-10-03T00:14:16-07:00] failed to detect region: RequestError: send request failed
caused by: Get http://169.254.169.254/latest/meta-data/placement/availability-zone: dial tcp 169.254.169.254:80: connect: host is down 
exit status 1
make: *** [run] Error 1
MTV:instance-manager nmogulla$ 
```

I will check alternative options to test it tomorrow and update here with the results
eytan-avisror: @mnkg561 Thanks, regarding your error, you should `export AWS_REGION=us-west-2` or other region in other to move forward from that.

Will add some comments you your PR shortly
CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/keikoproj/instance-manager?pullRequest=55) <br/>Thank you for your submission, we really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla-assistant.io/keikoproj/instance-manager?pullRequest=55) before we can accept your contribution.<br/><hr/>**nmogulla** seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please [add the email address used for this commit to your account](https://help.github.com/articles/why-are-my-commits-linked-to-the-wrong-user/#commits-are-not-linked-to-any-user).<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/keikoproj/instance-manager?pullRequest=55) it.</sub>
eytan-avisror: One more change needed is to copy-paste the contents of `config/crd/bases/instancemgr.keikoproj.io_instancegroups.yaml` and replace the CRD here:
https://github.com/keikoproj/instance-manager/blob/master/docs/04_instance-manager.yaml#L7

Same goes for `config/crd/bases/instance-manager-configmap.yaml` which should replace the template here:
https://github.com/keikoproj/instance-manager/blob/master/docs/04_instance-manager.yaml#L670

This file (04_instance-manager.yaml) is referenced in README.md so would be good to update it as well.
mnkg561: Testing went successful and could see the IAM role with extra managed policies attached

<img width="1333" alt="Screen Shot 2019-10-03 at 8 43 25 PM" src="https://user-images.githubusercontent.com/6040874/66179699-d779c580-e61e-11e9-9a9a-8e1c2637ce45.png">

Controller- logs

```
INFO[2019-10-03T20:37:42-07:00] reconcile started                            
INFO[2019-10-03T20:37:42-07:00] resource document version: 1060753           
INFO[2019-10-03T20:37:42-07:00] resource namespace: default                  
INFO[2019-10-03T20:37:42-07:00] resource name: naveen-test-with-iam-managed-policies 
INFO[2019-10-03T20:37:42-07:00] resource provisioner: eks-cf                 
INFO[2019-10-03T20:37:42-07:00] upgrade strategy: rollingupdate              
WARN[2019-10-03T20:37:42-07:00] using local kubernetes config                
WARN[2019-10-03T20:37:42-07:00] using local kubernetes config                
INFO[2019-10-03T20:37:42-07:00] state transitioned to: Init                  
INFO[2019-10-03T20:37:42-07:00] starting cloud discovery                     
INFO[2019-10-03T20:37:44-07:00] stack state: CREATE_COMPLETE                 
INFO[2019-10-03T20:37:44-07:00] starting state discovery                     
INFO[2019-10-03T20:37:44-07:00] state transitioned to: InitUpdate            
INFO[2019-10-03T20:37:44-07:00] starting update                              
INFO[2019-10-03T20:37:44-07:00] update not required                          
INFO[2019-10-03T20:37:44-07:00] state transitioned to: ReconcileModified     
INFO[2019-10-03T20:37:46-07:00] stack state: CREATE_COMPLETE                 
INFO[2019-10-03T20:37:46-07:00] starting bootstrap                           
INFO[2019-10-03T20:37:47-07:00] state transitioned to: Ready                 
INFO[2019-10-03T20:37:47-07:00] reconcile completed                          
2019-10-03T20:37:47.452-0700	DEBUG	controller-runtime.controller	Successfully Reconciled	{"controller": "instancegroup", "request": "default/naveen-test-with-iam-managed-policies"}

```
<img width="1254" alt="Screen Shot 2019-10-03 at 8 47 45 PM" src="https://user-images.githubusercontent.com/6040874/66179787-16a81680-e61f-11e9-8d26-8dbfc928ba1e.png">


mnkg561: There are couple of interesting things I observed

1. When you do a change on custom resource with kubectl (In my case, local) and if some reason credentials are expired, reconcile exited with "Completed Successfully" state and when I refreshed my credentials it didn't pick it up since it exited with Successfully. It only got picked up when I restart the controller which is expected

2. For some reason, couple of times all the existed custom resource CFNs got recreated when I restarted the controller and not the custom resource itself. What I mean is, I can see from controller logs and CFN console that its getting created but when I do
```
kubectl get ig --namespace=instance-manager
```
 I don't see a change in age.

 I have no idea when did they get deleted.

Here is the screenshot from CFN Console
<img width="436" alt="Screen Shot 2019-10-03 at 8 57 19 PM" src="https://user-images.githubusercontent.com/6040874/66180108-77841e80-e620-11e9-8585-39158c6e6595.png">

and kubectl response
<img width="1215" alt="Screen Shot 2019-10-03 at 8 59 00 PM" src="https://user-images.githubusercontent.com/6040874/66180156-a26e7280-e620-11e9-8562-2a8269e57b4a.png">

sorry for too many screenshots :)


eytan-avisror: @mnkg561 is it possible you are running that kubectl on different clusters? that second one doesnt look like a vanilla EKS cluster.
I'll take a look at all the changes
mnkg561: @eytan-avisror Looks like because of stack name changes. When I run it locally, there is no "namespace" name added to the stack name but in remote looks like we are adding the namespace name to the stack name. 
eytan-avisror: @mnkg561 Almost there, can you also make sure to sign the CLA:
https://cla-assistant.io/keikoproj/instance-manager?pullRequest=55

mnkg561: > @mnkg561 Almost there, can you also make sure to sign the CLA:
> https://cla-assistant.io/keikoproj/instance-manager?pullRequest=55

I singed it in the morning and also couple of times now but it still showing "Pending" state. I will wait for few mins
mnkg561: > @eytan-avisror Looks like because of stack name changes. When I run it locally, there is no "namespace" name added to the stack name but in remote looks like we are adding the namespace name to the stack name.

This brings up a new question for me, Should we store CFN name in the etcd so even if for some reason we end up changing the logic for stack name in future to include some other parameter it won't start creating new stacks? Well, more important (and basic) question, Can we store/update anything from controller with out triggering the event again after reconcile? May be "Status" section (No idea, I will probably read that section in more detail)
mnkg561: > [![CLA assistant check](https://camo.githubusercontent.com/0a16aa28ccc85529801cccef17b3dfaeb79183c6/68747470733a2f2f636c612d617373697374616e742e696f2f70756c6c2f62616467652f6e6f745f7369676e6564)](https://cla-assistant.io/keikoproj/instance-manager?pullRequest=55) 
> Thank you for your submission, we really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla-assistant.io/keikoproj/instance-manager?pullRequest=55) before we can accept your contribution.
> 
> **nmogulla** seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please [add the email address used for this commit to your account](https://help.github.com/articles/why-are-my-commits-linked-to-the-wrong-user/#commits-are-not-linked-to-any-user).
> You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/keikoproj/instance-manager?pullRequest=55) it.

Haa. This might be looking for work id "nmogulla" than my personal id "mnkg561". I did sign it with mnkg561 @eytan-avisror 
eytan-avisror: @mnkg561 You need to sign with any user that added commits to this PR, otherwise won't be able to merge
mnkg561: <img width="1045" alt="Screen Shot 2019-10-04 at 9 09 24 PM" src="https://user-images.githubusercontent.com/6040874/66249627-53d9da80-e6eb-11e9-8e5b-cf56f845f6d3.png">

@eytan-avisror CLA Assistant app might have issue with safari browser and its keep redirecting to signing page even after you sign it. Anyway, here is the screenshot from chrome about Agreement signature completion
eytan-avisror: @mnkg561 Afraid I cannot force merge this without CLA passing.
You will either need to send a new PR that contains commits by @mnkg561 (which is signed), or find a way to signed with `nmogulla` which committed the changes.

Maybe a new PR is easier - sorry for the trouble. 
mnkg561: Uff.. Finally. I had to go back and revoke permissions to CLA and sign it back and add my alias "nmogulla" to the profile. 

Thank you!! @eytan-avisror 

Patch:
diff --git a/.github/DEVELOPER.md b/.github/DEVELOPER.md
index ca3d262..173f23c 100644
--- a/.github/DEVELOPER.md
+++ b/.github/DEVELOPER.md
@@ -27,12 +27,17 @@ create
 Using the `Makefile` you can use `make run` to run instance-manager locally on your machine, and it will try to reconcile InstanceGroups in the cluster.
 Make sure the context you are in is correct and that you scaled down the actual instance-manager controller pod.
 
+1. You need to create 2 files for controller config and cfn and copy the data section from #config/crd/bases/instance-manager-configmap.yaml respectively
+2. Make sure you have AWS Credentials and region exported so that your local instance-manager controller can execute a cloud formation template
+
+
 ### Example
 
 ```bash
 $ kubectl scale deployment instance-manager --replicas 0
 deployment.extensions/instance-manager scaled
 
+
 $ make run
 /Users/eibissror/go/bin/controller-gen object:headerFile=./hack/boilerplate.go.txt paths=./api/...
 go fmt ./...
diff --git a/api/v1alpha1/instancegroup_types.go b/api/v1alpha1/instancegroup_types.go
index bda59ca..d25029c 100644
--- a/api/v1alpha1/instancegroup_types.go
+++ b/api/v1alpha1/instancegroup_types.go
@@ -173,6 +173,7 @@ type EKSCFConfiguration struct {
 	BootstrapArguments string              `json:"bootstrapArguments,omitempty"`
 	SpotPrice          string              `json:"spotPrice,omitempty"`
 	Tags               []map[string]string `json:"tags,omitempty"`
+	ManagedPolicies    []string            `json:"managedPolicies,omitempty"`
 }
 
 // InstanceGroupStatus defines the schema of resource Status
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index 4f6f848..232626d 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -81,6 +81,11 @@ func (in *EKSCFConfiguration) DeepCopyInto(out *EKSCFConfiguration) {
 			}
 		}
 	}
+	if in.ManagedPolicies != nil {
+		in, out := &in.ManagedPolicies, &out.ManagedPolicies
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EKSCFConfiguration.
diff --git a/config/crd/bases/instance-manager-configmap.yaml b/config/crd/bases/instance-manager-configmap.yaml
index 48bfe22..fec05d5 100644
--- a/config/crd/bases/instance-manager-configmap.yaml
+++ b/config/crd/bases/instance-manager-configmap.yaml
@@ -108,6 +108,9 @@ data:
       Subnets:
         Description: The subnets where workers can be created.
         Type: List<AWS::EC2::Subnet::Id>
+      ManagedPolicyARNs:
+        Description: List of Managed IAM Policy ARNs to be attached to instance groups
+        Type: List<String>
     Metadata:
       AWS::CloudFormation::Interface:
         ParameterGroups:
@@ -129,6 +132,7 @@ data:
               - NodeVolumeSize
               - KeyName
               - BootstrapArguments
+              - ManagedPolicyARNs
           -
             Label:
               default: "Worker Network Configuration"
@@ -155,10 +159,7 @@ data:
               Action:
               - sts:AssumeRole
           Path: "/"
-          ManagedPolicyArns:
-            - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
-            - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
-            - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
+          ManagedPolicyArns: !Ref ManagedPolicyARNs
       NodeGroup:
         Type: AWS::AutoScaling::AutoScalingGroup
         Properties:
diff --git a/config/crd/bases/instancemgr.keikoproj.io_instancegroups.yaml b/config/crd/bases/instancemgr.keikoproj.io_instancegroups.yaml
index 8e11ae3..47ccb81 100644
--- a/config/crd/bases/instancemgr.keikoproj.io_instancegroups.yaml
+++ b/config/crd/bases/instancemgr.keikoproj.io_instancegroups.yaml
@@ -435,6 +435,10 @@ spec:
                       type: string
                     keyPairName:
                       type: string
+                    managedPolicies:
+                      items:
+                        type: string
+                      type: array
                     securityGroups:
                       items:
                         type: string
diff --git a/config/samples/instancemgr_v1alpha1_instancegroup_with_managed_policies.yaml b/config/samples/instancemgr_v1alpha1_instancegroup_with_managed_policies.yaml
new file mode 100644
index 0000000..3c98568
--- /dev/null
+++ b/config/samples/instancemgr_v1alpha1_instancegroup_with_managed_policies.yaml
@@ -0,0 +1,33 @@
+apiVersion: instancemgr.keikoproj.io/v1alpha1
+kind: InstanceGroup
+metadata:
+  name: naveen-test-with-iam-managed-policies
+spec:
+  eks-cf:
+    configuration:
+      clusterName: eks-kdev2-k8s
+      image: ami-015288619490c53cd
+      instanceType: m5.large
+      keyPairName: eks-kdev2-k8s-keypair
+      managedPolicies:
+        - AmazonDocDBFullAccess
+        - AmazonCloudDirectoryFullAccess
+      securityGroups:
+        - sg-08453b229abb4bf
+        - sg-029965312626144
+        - sg-0f68186cedee6d7
+      subnets:
+        - subnet-0e8277599681557
+        - subnet-0ad16fed12136f1
+        - subnet-08b86b32e34cf87
+      tags:
+        - key: k8s.io/cluster-autoscaler/enabled
+          value: "true"
+        - key: kubernetes.io/cluster/eks-kdev2-k8s
+          value: owned
+      volSize: 80
+    maxSize: 2
+    minSize: 1
+  provisioner: eks-cf
+  strategy:
+    type: rollingUpdate
\ No newline at end of file
diff --git a/controllers/provisioners/ekscloudformation/ekscloudformation.go b/controllers/provisioners/ekscloudformation/ekscloudformation.go
index 5eef2f8..84a56f4 100644
--- a/controllers/provisioners/ekscloudformation/ekscloudformation.go
+++ b/controllers/provisioners/ekscloudformation/ekscloudformation.go
@@ -474,6 +474,7 @@ func (ctx *EksCfInstanceGroupContext) processParameters() error {
 		"BootstrapArguments":          bootstrapArgs,
 		"NodeGroupName":               ctx.AwsWorker.StackName,
 		"VpcId":                       ctx.VpcID,
+		"ManagedPolicyARNs":           getManagedPolicyARNs(specConfig.ManagedPolicies),
 	}
 
 	var parameters []*cloudformation.Parameter
@@ -488,3 +489,24 @@ func (ctx *EksCfInstanceGroupContext) processParameters() error {
 	ctx.parseTags()
 	return nil
 }
+
+//getManagedPolicyARNs constructs managed policy arns
+func getManagedPolicyARNs(pNames []string) string {
+	//This is for Managed Policy ARN list.
+	//First add the DEFAULT required policies to the list passed by user as part of custom resource
+	var managedPolicyARNs []string
+	requiredPolicies := []string{"AmazonEKSWorkerNodePolicy", "AmazonEKS_CNI_Policy", "AmazonEC2ContainerRegistryReadOnly"}
+	policyPrefix := "arn:aws:iam::aws:policy/"
+	for _, name := range requiredPolicies {
+		managedPolicyARNs = append(managedPolicyARNs, fmt.Sprintf("%s%s", policyPrefix, name))
+	}
+	// Add the user supplied policy names if any
+	if len(pNames) != 0 {
+		for _, name := range pNames {
+			// Trim the prefix arn:aws:iam::aws:policy/ if user supplied entire ARN instead of just name
+			managedPolicyARNs = append(managedPolicyARNs, fmt.Sprintf("%s%s", policyPrefix, strings.TrimPrefix(name, policyPrefix)))
+		}
+	}
+
+	return common.ConcatonateList(managedPolicyARNs, ",")
+}
diff --git a/docs/04_instance-manager.yaml b/docs/04_instance-manager.yaml
index b029178..04b793f 100644
--- a/docs/04_instance-manager.yaml
+++ b/docs/04_instance-manager.yaml
@@ -439,6 +439,10 @@ spec:
                       type: string
                     keyPairName:
                       type: string
+                    managedPolicies:
+                      items:
+                        type: string
+                      type: array
                     securityGroups:
                       items:
                         type: string
@@ -768,6 +772,9 @@ data:
       Subnets:
         Description: The subnets where workers can be created.
         Type: List<AWS::EC2::Subnet::Id>
+      ManagedPolicyARNs:
+        Description: List of Managed IAM Policy ARNs to be attached to instance groups
+        Type: List<String>
     Metadata:
       AWS::CloudFormation::Interface:
         ParameterGroups:
@@ -789,6 +796,7 @@ data:
               - NodeVolumeSize
               - KeyName
               - BootstrapArguments
+              - ManagedPolicyARNs
           -
             Label:
               default: "Worker Network Configuration"
@@ -815,10 +823,7 @@ data:
               Action:
               - sts:AssumeRole
           Path: "/"
-          ManagedPolicyArns:
-            - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
-            - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
-            - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
+          ManagedPolicyArns: !Ref ManagedPolicyARNs
       NodeGroup:
         Type: AWS::AutoScaling::AutoScalingGroup
         Properties:
diff --git a/go.sum b/go.sum
index 774e40b..87a41f8 100644
--- a/go.sum
+++ b/go.sum
@@ -71,6 +71,7 @@ github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
 github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
 github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
@@ -79,8 +80,6 @@ github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswD
 github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
 github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/keikoproj/aws-auth v0.0.0-20190903203752-40f0a783a042 h1:7CAKNomEvhv6H4mXGEdUxFJZR/pJgmKAAXWasO/7PMY=
-github.com/keikoproj/aws-auth v0.0.0-20190903203752-40f0a783a042/go.mod h1:TFnlFnL0EzKnV3lFZM/pTR/w4e/81kVJ/yd2gDaQk08=
 github.com/keikoproj/aws-auth v0.0.0-20190910182258-c705a9d52f92 h1:HcsujbGJfEC+aM/L48VXtfQnmPY1DVS3t7ylJZOZoqo=
 github.com/keikoproj/aws-auth v0.0.0-20190910182258-c705a9d52f92/go.mod h1:TFnlFnL0EzKnV3lFZM/pTR/w4e/81kVJ/yd2gDaQk08=
 github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=


Test patch:
diff --git a/controllers/provisioners/ekscloudformation/ekscloudformation_test.go b/controllers/provisioners/ekscloudformation/ekscloudformation_test.go
index 44d370b..c7ea365 100644
--- a/controllers/provisioners/ekscloudformation/ekscloudformation_test.go
+++ b/controllers/provisioners/ekscloudformation/ekscloudformation_test.go
@@ -1204,3 +1204,39 @@ defaultArns:
 		t.Fatalf("Test_ControllerConfigLoader: got %v, expected: %v", specConfig.GetSubnets(), expectedConfig.DefaultSubnets)
 	}
 }
+
+func TestGetManagedPolicyARNs(t *testing.T) {
+	tt := []struct {
+		testCase string
+		input    []string
+		expected string
+	}{
+		{
+			testCase: "custom resource creation with no managed policies",
+			input:    []string{},
+			expected: "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy,arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy,arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly",
+		},
+		{
+			testCase: "custom resource creation with 1 managed policies",
+			input:    []string{"AWSDynamoDBFullAccess"},
+			expected: "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy,arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy,arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly,arn:aws:iam::aws:policy/AWSDynamoDBFullAccess",
+		},
+		{
+			testCase: "custom resource creation with multiple managed policies",
+			input:    []string{"My-Managed-Policy-1", "My-Managed-Policy-2", "My-Managed-Policy-3"},
+			expected: "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy,arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy,arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly,arn:aws:iam::aws:policy/My-Managed-Policy-1,arn:aws:iam::aws:policy/My-Managed-Policy-2,arn:aws:iam::aws:policy/My-Managed-Policy-3",
+		},
+		{
+			testCase: "custom resource creation with managed policy arn instead of name",
+			input:    []string{"arn:aws:iam::aws:policy/My-Managed-Policy-1"},
+			expected: "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy,arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy,arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly,arn:aws:iam::aws:policy/My-Managed-Policy-1",
+		},
+	}
+
+	for _, tc := range tt {
+		resp := getManagedPolicyARNs(tc.input)
+		if !reflect.DeepEqual(resp, tc.expected) {
+			t.Fatalf("Test Case [%s] Failed Expected [%s] Got [%s]\n", tc.testCase, tc.expected, resp)
+		}
+	}
+}