Repository: kubernetes-sigs/kro

Issue:
Install using kubectl and raw manifests
### Documentation Issue

<!-- Please craft a detailed description of the documentation issue or suggestion -->
<!-- Below are some suggestions but feel free to modify or remove them -->

**Location**:
https://kro.run/docs/getting-started/Installation

**Current State**:
Install using helm 

**Suggested Changes**:
Release scripts to push manifests somewhere 
And docs point to usage. 

**Additional Context**:
<!-- Any additional information that might be helpful -->


Hints from issue comments:
@chamodshehanka: I like to work on this @tjamet: @chamodshehanka happy to pair
did you have the chance to take a look at #732 ?@chamodshehanka: > [@chamodshehanka](https://github.com/chamodshehanka) happy to pair did you have the chance to take a look at [#732](https://github.com/kubernetes-sigs/kro/pull/732) ?

sure I'll check that out 

Solution patch:
diff --git a/.github/workflows/github-release.yaml b/.github/workflows/github-release.yaml
index 72eb218..1cf7611 100644
--- a/.github/workflows/github-release.yaml
+++ b/.github/workflows/github-release.yaml
@@ -13,6 +13,14 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
+      - name: Build static manifests
+        env:
+          TAG: ${{ github.ref_name}}
+        run: |
+          make render-static-manifests RELEASE_VERSION=${TAG}
+
       - uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # 2.4.1
         with:
           generate_release_notes: true
+          files: |
+            ./manifests/rendered/kro-*.yaml
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index e13ee5e..0765b48 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,3 +44,4 @@ TODO.*
 node_modules/
 
 tools/lsp/client/out/
+manifests/rendered
diff --git a/Makefile b/Makefile
index 39fc5a5..dd66a4c 100644
--- a/Makefile
+++ b/Makefile
@@ -44,6 +44,15 @@ LDFLAGS="-buildid= -X sigs.k8s.io/release-utils/version.gitVersion=$(GIT_VERSION
 
 WITH_GOFLAGS = GOFLAGS="$(GOFLAGS)"
 
+HELM_STATIC_MANIFESTS_FLAGS ?= --set metadata.includeHelmChart=false --set metadata.includeManagedBy=false --include-crds --namespace kro-system
+HELM_STATIC_MANIFEST_IMAGE_FLAGS ?= --set image.tag=${RELEASE_VERSION}
+
+ifeq ($(shell uname -s),Darwin)
+	SED_INPLACE_FLAGS ?= -i ''
+else
+	SED_INPLACE_FLAGS ?= -i
+endif
+
 HELM_DIR = ./helm
 WHAT ?= unit
 
@@ -228,17 +237,33 @@ publish-image: ko ## Publish the kro controller images
 		$(KO) publish --bare github.com/kubernetes-sigs/kro/cmd/controller \
 		--tags ${RELEASE_VERSION} --sbom=none
 
+.PHONY: inject-helm-version
+inject-helm-version:
+	sed $(SED_INPLACE_FLAGS) 's/tag: .*/tag: "$(RELEASE_VERSION)"/' helm/values.yaml
+	sed $(SED_INPLACE_FLAGS) 's/version: .*/version: $(RELEASE_VERSION)/' helm/Chart.yaml
+	sed $(SED_INPLACE_FLAGS) 's/appVersion: .*/appVersion: "$(RELEASE_VERSION)"/' helm/Chart.yaml
+
 .PHONY: package-helm
-package-helm: ## Package Helm chart
-	sed -i 's/tag: .*/tag: "$(RELEASE_VERSION)"/' helm/values.yaml
-	sed -i 's/version: .*/version: $(RELEASE_VERSION)/' helm/Chart.yaml
-	sed -i 's/appVersion: .*/appVersion: "$(RELEASE_VERSION)"/' helm/Chart.yaml
+package-helm: inject-helm-version ## Package Helm chart
 	${HELM} package helm
 
 .PHONY: publish-helm
 publish-helm: ## Helm publish
 	${HELM} push ./kro-${RELEASE_VERSION}.tgz oci://${HELM_IMAGE}
 
+.PHONY: render-static-manifests
+render-static-manifests: inject-helm-version
+	mkdir -p manifests/rendered
+	@command -v yq >/dev/null 2>&1 || { echo >&2 "yq is required but not installed. Please install yq v4+"; exit 1; }
+	@variants=$$(yq -r '.variants[].name' manifests/variants.yaml); \
+	for v in $$variants; do \
+		echo "Rendering variant: $$v"; \
+		tmpfile=$$(mktemp); \
+		yq -r ".variants[] | select(.name==\"$${v}\") | .values" manifests/variants.yaml > $$tmpfile; \
+		${HELM} template ${HELM_STATIC_MANIFESTS_FLAGS} ${HELM_STATIC_MANIFEST_IMAGE_FLAGS} -f $$tmpfile kro ./helm > manifests/rendered/$${v}.yaml; \
+		rm -f $$tmpfile; \
+	done
+
 .PHONY:
 release: build-image publish-image package-helm publish-helm
 
@@ -256,12 +281,15 @@ install: manifests ## Install CRDs into the K8s cluster specified in ~/.kube/con
 uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config
 	$(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f ./helm/crds
 
-.PHONY: deploy-kind
-deploy-kind: export KO_DOCKER_REPO=kind.local
-deploy-kind: ko ## Deploy kro to a kind cluster
-	$(KIND) delete clusters ${KIND_CLUSTER_NAME} || true
+.PHONY: start-kind
+start-kind:
+	$(KIND) delete cluster --name ${KIND_CLUSTER_NAME} || true
 	$(KIND) create cluster --name ${KIND_CLUSTER_NAME}
-	$(KUBECTL) --context kind-$(KIND_CLUSTER_NAME) create namespace kro-system
+	$(KUBECTL) --context kind-${KIND_CLUSTER_NAME} create namespace kro-system
+
+.PHONY: deploy-kind-helm
+deploy-kind-helm: export KO_DOCKER_REPO=kind.local
+deploy-kind-helm: ko start-kind
 	make install
 	# This generates deployment with ko://... used in image.
 	# ko then intercepts it builds image, pushes to kind node, replaces the image in deployment and applies it
@@ -269,6 +297,15 @@ deploy-kind: ko ## Deploy kro to a kind cluster
 	kubectl wait --for=condition=ready --timeout=1m pod -n kro-system -l app.kubernetes.io/component=controller
 	$(KUBECTL) --context kind-${KIND_CLUSTER_NAME} get pods -A
 
+.PHONY: deploy-kind-%
+deploy-kind-%: export KO_DOCKER_REPO=kind.local
+deploy-kind-%: export HELM_STATIC_MANIFEST_IMAGE_FLAGS=--set image.pullPolicy=Never --set image.ko=true
+deploy-kind-%: export RELEASE_VERSION=v0.0.0-dev
+deploy-kind-%: ko start-kind render-static-manifests ## Apply the static manifests for the given variant
+	$(KO) apply -f manifests/rendered/kro-$*.yaml
+	kubectl wait --for=condition=ready --timeout=1m pod -n kro-system -l app.kubernetes.io/component=controller
+	$(KUBECTL) --context kind-${KIND_CLUSTER_NAME} get pods -A
+
 .PHONY: ko-apply
 ko-apply: ko
 	${HELM} template kro ./helm --namespace kro-system --set image.pullPolicy=Never --set image.ko=true | $(KO) apply -f -
@@ -286,6 +323,18 @@ cli:
 test-e2e: chainsaw ## Run e2e tests
 	$(CHAINSAW) test ./test/e2e/chainsaw
 
+
+.PHONY: test-e2e-kind-%
+test-e2e-kind-%: deploy-kind-%
+	make test-e2e
+
+
+# Default deployment uses helm deployments
+
+.PHONY: deploy-kind
+deploy-kind: export KO_DOCKER_REPO=kind.local
+deploy-kind: ko deploy-kind-helm ## Deploy kro to a kind cluster
+
+# Default end to end tests uses helm deployments
 .PHONY: test-e2e-kind
-test-e2e-kind: deploy-kind
-	make test-e2e
\ No newline at end of file
+test-e2e-kind: deploy-kind-helm
\ No newline at end of file
diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl
index 828c129..6c1712c 100644
--- a/helm/templates/_helpers.tpl
+++ b/helm/templates/_helpers.tpl
@@ -45,12 +45,16 @@ Create the name of the service account to use
 Common labels
 */}}
 {{- define "kro.labels" -}}
+{{- if .Values.metadata.includeHelmChart }}
 helm.sh/chart: {{ include "kro.chart" . }}
+{{- end }}
 {{ include "kro.selectorLabels" . }}
 {{- if .Chart.AppVersion }}
 app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
 {{- end }}
+{{- if .Values.metadata.includeManagedBy }}
 app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
 app.kubernetes.io/component: controller
 app.kubernetes.io/part-of: kro
 {{- if .Values.additionalLabels }}
diff --git a/helm/values.yaml b/helm/values.yaml
index f811005..9470d6a 100644
--- a/helm/values.yaml
+++ b/helm/values.yaml
@@ -7,6 +7,10 @@ fullnameOverride: ""
 additionalLabels: {}
   # app: kro
 
+metadata:
+  includeHelmChart: true
+  includeManagedBy: true
+
 image:
   # The location of the container image repository
   repository: registry.k8s.io/kro/kro
diff --git a/manifests/kustomization.yaml b/manifests/kustomization.yaml
new file mode 100644
index 0000000..b8e11c7
--- /dev/null
+++ b/manifests/kustomization.yaml
@@ -0,0 +1,2 @@
+resources:
+- ./bundled/kro-core-install-manifests.yaml
\ No newline at end of file
diff --git a/manifests/variants.yaml b/manifests/variants.yaml
new file mode 100644
index 0000000..2f83155
--- /dev/null
+++ b/manifests/variants.yaml
@@ -0,0 +1,14 @@
+variants:
+- name: kro-core-install-manifests
+  values:
+    rbac:
+      mode: aggregation
+- name: kro-core-install-manifests-with-prometheus
+  values:
+    rbac:
+      mode: aggregation
+    metrics:
+      service:
+        create: true
+      serviceMonitor:
+        enabled: true
\ No newline at end of file
diff --git a/scripts/ci/presubmits/e2e-raw-manifests-tests b/scripts/ci/presubmits/e2e-raw-manifests-tests
new file mode 100755
index 0000000..b570144
--- /dev/null
+++ b/scripts/ci/presubmits/e2e-raw-manifests-tests
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}
+
+# Build
+go build cmd
+
+mkdir -p ./bin
+curl -Lo ./bin/kind https://kind.sigs.k8s.io/dl/v0.30.0/kind-linux-amd64
+chmod +x ./bin/kind
+
+# Raw manifests based end to end tests, with minimal installation and "aggregated" mode
+make test-e2e-kind-core-install-manifests KIND=./bin/kind
\ No newline at end of file
diff --git a/scripts/ci/presubmits/e2e-tests b/scripts/ci/presubmits/e2e-tests
index 2297a14..33499a6 100755
--- a/scripts/ci/presubmits/e2e-tests
+++ b/scripts/ci/presubmits/e2e-tests
@@ -14,4 +14,5 @@ mkdir -p ./bin
 curl -Lo ./bin/kind https://kind.sigs.k8s.io/dl/v0.30.0/kind-linux-amd64
 chmod +x ./bin/kind
 
+# Helm based end to end tests, with helm defaults
 make test-e2e-kind KIND=./bin/kind
diff --git a/website/docs/docs/getting-started/01-Installation.md b/website/docs/docs/getting-started/01-Installation.md
index 0c4a30b..49d283b 100644
--- a/website/docs/docs/getting-started/01-Installation.md
+++ b/website/docs/docs/getting-started/01-Installation.md
@@ -11,7 +11,9 @@ Install kro on your Kubernetes cluster using Helm.
 
 ## Prerequisites
 
-- Helm 3.x installed
+Kro can be installed via Helm or raw manifests. Pick the solution that best fits you.
+
+- Helm 3.x installed (only for helm installation)
 - kubectl configured to access your cluster
 
 ## Installation
@@ -23,7 +25,7 @@ Install the latest version:
 
 ```bash
 helm install kro oci://registry.k8s.io/kro/charts/kro \
-  --namespace kro \
+  --namespace kro-system \
   --create-namespace
 ```
 
@@ -38,7 +40,7 @@ export KRO_VERSION=0.6.2
 
 # Install kro
 helm install kro oci://registry.k8s.io/kro/charts/kro \
-  --namespace kro \
+  --namespace kro-system \
   --create-namespace \
   --version=${KRO_VERSION}
 ```
@@ -56,40 +58,88 @@ echo $KRO_VERSION
 
 # Install kro
 helm install kro oci://registry.k8s.io/kro/charts/kro \
-  --namespace kro \
+  --namespace kro-system \
   --create-namespace \
   --version=${KRO_VERSION}
 ```
 
   </TabItem>
+  <TabItem value="kubectl" label="Raw manifest installation" default>
+
+Select which pre-built flavour you would like to install
+
+| variant                                    | Prometheus scrape config |
+| ------------------------------------------ | ------------------------ |
+| kro-core-install-manifests                 | Not installed            |
+| kro-core-install-manifests-with-prometheus | Installed                |
+
+Fetch the latest release version from GitHub
+
+```sh
+export KRO_VERSION=$(curl -sL \
+      https://api.github.com/repos/kubernetes-sigs/kro/releases/latest | \
+      jq -r '.tag_name | ltrimstr("v")'
+   )
+export KRO_VARIANT=kro-core-install-manifests
+
+echo $KRO_VERSION
+```
+
+```bash
+kubectl create namespace kro-system
+kubectl apply -f https://github.com/kubernetes-sigs/kro/releases/download/$KRO_VERSION/$KRO_VARIANT.yaml
+```
+  </TabItem>
 </Tabs>
 
 ## Verify Installation
 
+<Tabs>
+  <TabItem value="helm" label="Helm installation" default>
 Check the Helm release:
 
 ```bash
-helm list -n kro
+helm list -n kro-system
 ```
 
+
 Expected output:
 ```
 NAME  NAMESPACE  REVISION  STATUS
-kro   kro        1         deployed
+kro   kro-system 1         deployed
 ```
 
 Check the kro pod is running:
 
 ```bash
-kubectl get pods -n kro
+kubectl get pods -n kro-system
 ```
 
 Expected output:
 ```
 NAME                   READY   STATUS    RESTARTS   AGE
 kro-7d98bc6f46-jvjl5   1/1     Running   0          30s
+```
+  </TabItem>
+  <TabItem value="kubectl" label="Raw manifest installation">
+Check the Helm release:
+
+```bash
+
+Check the kro pod is running:
+
+```bash
+kubectl get pods -n kro-system
 ```
 
+Expected output:
+```
+NAME                   READY   STATUS    RESTARTS   AGE
+kro-7d98bc6f46-jvjl5   1/1     Running   0          30s
+```
+  </TabItem>
+</Tabs>
+
 ## Upgrade
 
 <Tabs>
@@ -97,7 +147,7 @@ kro-7d98bc6f46-jvjl5   1/1     Running   0          30s
 
 ```bash
 helm upgrade kro oci://registry.k8s.io/kro/charts/kro \
-  --namespace kro
+  --namespace kro-system
 ```
 
   </TabItem>
@@ -107,22 +157,51 @@ helm upgrade kro oci://registry.k8s.io/kro/charts/kro \
 export KRO_VERSION=0.6.2
 
 helm upgrade kro oci://registry.k8s.io/kro/charts/kro \
-  --namespace kro \
+  --namespace kro-system \
   --version=${KRO_VERSION}
 ```
 
-  </TabItem>
-</Tabs>
-
 :::info
 Helm does not update CRDs automatically. If a new version includes CRD changes, you may need to manually apply them. Check the release notes for CRD updates.
 :::
+  </TabItem>
+  <TabItem value="kubectl" label="Raw manifest installation">
+```
+export KRO_VERSION=$(curl -sL \
+      https://api.github.com/repos/kubernetes-sigs/kro/releases/latest | \
+      jq -r '.tag_name | ltrimstr("v")'
+   )
+export KRO_VARIANT=kro-core-install-manifests
+```
+
+```
+kubectl apply -f https://github.com/kubernetes-sigs/kro/releases/download/$KRO_VERSION/$KRO_VARIANT.yaml
+```
+
+:::info[**Removal of dangling objects**]
+
+Kubectl installation does remove old objects. You may need to manually remove any
+object that was removed in the new version of Kro.
+:::
+
+  </TabItem>
+</Tabs>
 
 ## Uninstall
 
+
+<Tabs>
+  <TabItem value="helm" label="Helm installation" default>
+```bash
+helm uninstall kro -n kro-system
+```
+  </TabItem>
+  <TabItem value="kubectl" label="Raw manifest installation">
 ```bash
-helm uninstall kro -n kro
+kubectl delete -f https://github.com/kubernetes-sigs/kro/releases/download/$KRO_VERSION/$KRO_VARIANT.yaml
 ```
+  </TabItem>
+</Tabs>
 
 :::info
 This removes the kro controller but preserves your RGDs and deployed instances. To fully clean up, manually delete instances and RGDs before uninstalling.



Test patch:
diff --git a/test/e2e/chainsaw/check-arbitary-objects/chainsaw-test.yaml b/test/e2e/chainsaw/check-arbitary-objects/chainsaw-test.yaml
index 8314acb..89031f2 100644
--- a/test/e2e/chainsaw/check-arbitary-objects/chainsaw-test.yaml
+++ b/test/e2e/chainsaw/check-arbitary-objects/chainsaw-test.yaml
@@ -9,6 +9,9 @@ spec:
   - name: install-rgd
     try:
     #description: Install the RGD that creates an Instance CRD
+    - apply:
+        file: cluster-role.yaml
+      description: Apply Cluster Role
     - apply:
         file: rgd.yaml
       description: Apply RGD
diff --git a/test/e2e/chainsaw/check-arbitary-objects/cluster-role.yaml b/test/e2e/chainsaw/check-arbitary-objects/cluster-role.yaml
new file mode 100644
index 0000000..a739caf
--- /dev/null
+++ b/test/e2e/chainsaw/check-arbitary-objects/cluster-role.yaml
@@ -0,0 +1,19 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: kro:controller:check-arbitary-objects
+  labels:
+    rbac.kro.run/aggregate-to-controller: "true"
+rules:
+  - apiGroups:
+    - "kro.run"
+    resources:
+    - "checkarbitraryobjectsnoops"
+    - "checkarbitraryobjectsnoops/status"
+    verbs:
+    - "create"
+    - "get"
+    - "list"
+    - "watch"
+    - "update"
+    - "patch"
\ No newline at end of file
diff --git a/test/e2e/chainsaw/check-external-references/chainsaw-test.yaml b/test/e2e/chainsaw/check-external-references/chainsaw-test.yaml
index b4817b2..a2496c7 100644
--- a/test/e2e/chainsaw/check-external-references/chainsaw-test.yaml
+++ b/test/e2e/chainsaw/check-external-references/chainsaw-test.yaml
@@ -8,6 +8,9 @@ spec:
   steps:
   - name: setup
     try:
+    - apply:
+        file: cluster-role.yaml
+      description: Apply Cluster Role
     - apply:
         file: rgd.yaml
       description: Apply RGD
diff --git a/test/e2e/chainsaw/check-external-references/cluster-role.yaml b/test/e2e/chainsaw/check-external-references/cluster-role.yaml
new file mode 100644
index 0000000..483e665
--- /dev/null
+++ b/test/e2e/chainsaw/check-external-references/cluster-role.yaml
@@ -0,0 +1,31 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: kro:controller:check-external-references
+  labels:
+    rbac.kro.run/aggregate-to-controller: "true"
+rules:
+  - apiGroups:
+    - "kro.run"
+    resources:
+    - "externalreferencetests"
+    - "externalreferencetests/status"
+    verbs:
+    - "create"
+    - "get"
+    - "list"
+    - "watch"
+    - "update"
+    - "patch"
+  - apiGroups:
+    - ""
+    resources:
+    - "configmaps"
+    verbs:
+    - "create"
+    - "get"
+    - "list"
+    - "watch"
+    - "update"
+    - "patch"
+    - "delete"
\ No newline at end of file
diff --git a/test/e2e/chainsaw/check-instance-creation/chainsaw-test.yaml b/test/e2e/chainsaw/check-instance-creation/chainsaw-test.yaml
index 7f2bbce..1a27e56 100644
--- a/test/e2e/chainsaw/check-instance-creation/chainsaw-test.yaml
+++ b/test/e2e/chainsaw/check-instance-creation/chainsaw-test.yaml
@@ -9,6 +9,9 @@ spec:
   - name: install-rgd
     try:
     #description: Install the RGD that creates an Instance CRD
+    - apply:
+        file: cluster-role.yaml
+      description: Apply Cluster Role
     - apply:
         file: rgd.yaml
       description: Apply RGD
diff --git a/test/e2e/chainsaw/check-instance-creation/cluster-role.yaml b/test/e2e/chainsaw/check-instance-creation/cluster-role.yaml
new file mode 100644
index 0000000..1ffad14
--- /dev/null
+++ b/test/e2e/chainsaw/check-instance-creation/cluster-role.yaml
@@ -0,0 +1,31 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: kro:controller:check-instance-creation
+  labels:
+    rbac.kro.run/aggregate-to-controller: "true"
+rules:
+  - apiGroups:
+    - "kro.run"
+    resources:
+    - "checkinstancecreationsimpledeployments"
+    - "checkinstancecreationsimpledeployments/status"
+    verbs:
+    - "create"
+    - "get"
+    - "list"
+    - "watch"
+    - "update"
+    - "patch"
+  - apiGroups:
+    - apps
+    resources:
+    - deployments
+    verbs:
+    - "create"
+    - "get"
+    - "list"
+    - "watch"
+    - "update"
+    - "patch"
+    - "delete"
\ No newline at end of file
diff --git a/test/e2e/chainsaw/check-instance-update/chainsaw-test.yaml b/test/e2e/chainsaw/check-instance-update/chainsaw-test.yaml
index 2082992..51981b3 100644
--- a/test/e2e/chainsaw/check-instance-update/chainsaw-test.yaml
+++ b/test/e2e/chainsaw/check-instance-update/chainsaw-test.yaml
@@ -9,6 +9,9 @@ spec:
   - name: install-rgd
     try:
     #description: Install the RGD that creates an Instance CRD
+    - apply:
+        file: cluster-role.yaml
+      description: Apply Cluster Role
     - apply:
         file: rgd.yaml
       description: Apply RGD
diff --git a/test/e2e/chainsaw/check-instance-update/cluster-role.yaml b/test/e2e/chainsaw/check-instance-update/cluster-role.yaml
new file mode 100644
index 0000000..a71ce9d
--- /dev/null
+++ b/test/e2e/chainsaw/check-instance-update/cluster-role.yaml
@@ -0,0 +1,31 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: kro:controller:check-instance-update
+  labels:
+    rbac.kro.run/aggregate-to-controller: "true"
+rules:
+  - apiGroups:
+    - "kro.run"
+    resources:
+    - "checkinstanceupdatesimpledeployments"
+    - "checkinstanceupdatesimpledeployments/status"
+    verbs:
+    - "create"
+    - "get"
+    - "list"
+    - "watch"
+    - "update"
+    - "patch"
+  - apiGroups:
+    - apps
+    resources:
+    - deployments
+    verbs:
+    - "create"
+    - "get"
+    - "list"
+    - "watch"
+    - "update"
+    - "patch"
+    - "delete"
\ No newline at end of file
diff --git a/test/e2e/chainsaw/check-multi-resource-rgd/chainsaw-test.yaml b/test/e2e/chainsaw/check-multi-resource-rgd/chainsaw-test.yaml
index ba7dc0f..c923a2d 100644
--- a/test/e2e/chainsaw/check-multi-resource-rgd/chainsaw-test.yaml
+++ b/test/e2e/chainsaw/check-multi-resource-rgd/chainsaw-test.yaml
@@ -9,6 +9,9 @@ spec:
   - name: install-rgd
     try:
     #description: Install the RGD that creates multiple resources
+    - apply:
+        file: cluster-role.yaml
+      description: Apply Cluster Role
     - apply:
         file: rgd.yaml
       description: Apply RGD
diff --git a/test/e2e/chainsaw/check-multi-resource-rgd/cluster-role.yaml b/test/e2e/chainsaw/check-multi-resource-rgd/cluster-role.yaml
new file mode 100644
index 0000000..74ff4c1
--- /dev/null
+++ b/test/e2e/chainsaw/check-multi-resource-rgd/cluster-role.yaml
@@ -0,0 +1,56 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: kro:controller:check-multi-resource-rgd
+  labels:
+    rbac.kro.run/aggregate-to-controller: "true"
+rules:
+  - apiGroups:
+    - "kro.run"
+    resources:
+    - "checkmultiresourceautoscaleddeployments"
+    - "checkmultiresourceautoscaleddeployments/status"
+    verbs:
+    - "create"
+    - "get"
+    - "list"
+    - "watch"
+    - "update"
+    - "patch"
+  - apiGroups:
+    - ""
+    resources:
+    - "configmaps"
+    - "services"
+    verbs:
+    - "create"
+    - "get"
+    - "list"
+    - "watch"
+    - "update"
+    - "patch"
+    - "delete"
+  - apiGroups:
+    - apps
+    resources:
+    - deployments
+    verbs:
+    - "create"
+    - "get"
+    - "list"
+    - "watch"
+    - "update"
+    - "patch"
+    - "delete"
+  - apiGroups:
+    - autoscaling
+    resources:
+    - horizontalpodautoscalers
+    verbs:
+    - "create"
+    - "get"
+    - "list"
+    - "watch"
+    - "update"
+    - "patch"
+    - "delete"
\ No newline at end of file
diff --git a/test/e2e/chainsaw/check-rgd-creation/chainsaw-test.yaml b/test/e2e/chainsaw/check-rgd-creation/chainsaw-test.yaml
index d88c3e3..f86f22a 100644
--- a/test/e2e/chainsaw/check-rgd-creation/chainsaw-test.yaml
+++ b/test/e2e/chainsaw/check-rgd-creation/chainsaw-test.yaml
@@ -9,6 +9,9 @@ spec:
   - name: install-rgd
     try:
     #description: Install the RGD that creates an Instance CRD
+    - apply:
+        file: cluster-role.yaml
+      description: Apply Cluster Role
     - apply:
         file: rgd.yaml
       description: Apply RGD
diff --git a/test/e2e/chainsaw/check-rgd-creation/cluster-role.yaml b/test/e2e/chainsaw/check-rgd-creation/cluster-role.yaml
new file mode 100644
index 0000000..1469df6
--- /dev/null
+++ b/test/e2e/chainsaw/check-rgd-creation/cluster-role.yaml
@@ -0,0 +1,31 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: kro:controller:check-rgd-creation
+  labels:
+    rbac.kro.run/aggregate-to-controller: "true"
+rules:
+  - apiGroups: 
+    - "kro.run"
+    resources:
+    - "checkrgdcreationsimpledeployments"
+    - "checkrgdcreationsimpledeployments/status"
+    verbs:
+    - "create"
+    - "get"
+    - "list"
+    - "watch"
+    - "update"
+    - "patch"
+  - apiGroups:
+    - apps
+    resources:
+    - deployments
+    verbs:
+    - "create"
+    - "get"
+    - "list"
+    - "watch"
+    - "update"
+    - "patch"
+    - "delete"
\ No newline at end of file
diff --git a/test/e2e/chainsaw/check-rgd-deletion/chainsaw-test.yaml b/test/e2e/chainsaw/check-rgd-deletion/chainsaw-test.yaml
index c0e4747..4aa645d 100644
--- a/test/e2e/chainsaw/check-rgd-deletion/chainsaw-test.yaml
+++ b/test/e2e/chainsaw/check-rgd-deletion/chainsaw-test.yaml
@@ -3,12 +3,15 @@ kind: Test
 metadata:
   name: check-rgd-deletion
 spec:
-  description: | 
-    Tests if deleting a ResourceGraphDefinition deletes the Instance CRD
+  description: |
+    Tests if deleting a ResourceGraphDefinition properly cleans up resources (with CRD deletion disabled, CRD should persist)
   steps:
   - name: install-rgd
     try:
     #- description: Install the RGD that creates an Instance CRD
+    - apply:
+        file: cluster-role.yaml
+      description: Apply Cluster Role
     - apply:
         file: rgd.yaml
       description: Apply RGD
@@ -46,10 +49,10 @@ spec:
     - description: sleep operation
       sleep:
         duration: 20s # wait for the RGD to be deleted. Running the test with 5s is flaky (passes locally but fails in CI)
-  - name: check-that-crd-is-deleted
+  - name: check-that-crd-still-exists
     try:
-    - error:
-        file: instancecrd-assert.yaml        
+    - assert:
+        file: instancecrd-assert.yaml
     catch:
     - description: kro controller pod logs collector
       podLogs:
diff --git a/test/e2e/chainsaw/check-rgd-deletion/cluster-role.yaml b/test/e2e/chainsaw/check-rgd-deletion/cluster-role.yaml
new file mode 100644
index 0000000..69a323b
--- /dev/null
+++ b/test/e2e/chainsaw/check-rgd-deletion/cluster-role.yaml
@@ -0,0 +1,31 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: kro:controller:check-rgd-deletion
+  labels:
+    rbac.kro.run/aggregate-to-controller: "true"
+rules:
+  - apiGroups:
+    - "kro.run"
+    resources:
+    - "simpledeploymentfordeletions"
+    - "simpledeploymentfordeletions/status"
+    verbs:
+    - "create"
+    - "get"
+    - "list"
+    - "watch"
+    - "update"
+    - "patch"
+  - apiGroups:
+    - apps
+    resources:
+    - deployments
+    verbs:
+    - "create"
+    - "get"
+    - "list"
+    - "watch"
+    - "update"
+    - "patch"
+    - "delete"
\ No newline at end of file
diff --git a/test/e2e/chainsaw/check-unknown-fields/chainsaw-test.yaml b/test/e2e/chainsaw/check-unknown-fields/chainsaw-test.yaml
index fd459ee..d997c34 100644
--- a/test/e2e/chainsaw/check-unknown-fields/chainsaw-test.yaml
+++ b/test/e2e/chainsaw/check-unknown-fields/chainsaw-test.yaml
@@ -8,6 +8,9 @@ spec:
   steps:
   - name: setup
     try:
+    - apply:
+        file: cluster-role.yaml
+      description: Apply Cluster Role
     - apply:
         file: allow-unknown-crd.yaml
       description: Apply CRD with field that allows unknown fields
diff --git a/test/e2e/chainsaw/check-unknown-fields/cluster-role.yaml b/test/e2e/chainsaw/check-unknown-fields/cluster-role.yaml
new file mode 100644
index 0000000..4f98e10
--- /dev/null
+++ b/test/e2e/chainsaw/check-unknown-fields/cluster-role.yaml
@@ -0,0 +1,34 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: kro:controller:check-unknown-fields
+  labels:
+    rbac.kro.run/aggregate-to-controller: "true"
+rules:
+  - apiGroups:
+    - "kro.run"
+    resources:
+    - "allowunknowninstances"
+    - "allowunknowninstances/status"
+    - "allowunknowns"
+    - "allowunknowns/status"
+    verbs:
+    - "create"
+    - "get"
+    - "list"
+    - "watch"
+    - "update"
+    - "patch"
+    - "delete"
+  - apiGroups:
+    - ""
+    resources:
+    - "configmaps"
+    verbs:
+    - "create"
+    - "get"
+    - "list"
+    - "watch"
+    - "update"
+    - "patch"
+    - "delete"
\ No newline at end of file
diff --git a/test/e2e/chainsaw/check-validation-markers/chainsaw-test.yaml b/test/e2e/chainsaw/check-validation-markers/chainsaw-test.yaml
index 61acbdf..f54b093 100644
--- a/test/e2e/chainsaw/check-validation-markers/chainsaw-test.yaml
+++ b/test/e2e/chainsaw/check-validation-markers/chainsaw-test.yaml
@@ -9,6 +9,9 @@ spec:
   - name: install-rgd
     try:
     #description: Install the RGD that creates an Instance CRD with validation markers
+    - apply:
+        file: cluster-role.yaml
+      description: Apply Cluster Role
     - apply:
         file: rgd.yaml
       description: Apply RGD with validation markers
diff --git a/test/e2e/chainsaw/check-validation-markers/cluster-role.yaml b/test/e2e/chainsaw/check-validation-markers/cluster-role.yaml
new file mode 100644
index 0000000..05c0948
--- /dev/null
+++ b/test/e2e/chainsaw/check-validation-markers/cluster-role.yaml
@@ -0,0 +1,31 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: kro:controller:check-validation-markers
+  labels:
+    rbac.kro.run/aggregate-to-controller: "true"
+rules:
+  - apiGroups:
+    - "kro.run"
+    resources:
+    - "validationtestapps"
+    - "validationtestapps/status"
+    verbs:
+    - "create"
+    - "get"
+    - "list"
+    - "watch"
+    - "update"
+    - "patch"
+  - apiGroups:
+    - ""
+    resources:
+    - "configmaps"
+    verbs:
+    - "create"
+    - "get"
+    - "list"
+    - "watch"
+    - "update"
+    - "patch"
+    - "delete"
\ No newline at end of file