diff --git a/src/main/helm/templates/_helpers.tpl b/src/main/helm/templates/_helpers.tpl
index e0c2dd5afd501e2ecfa2327feb32faa20edf8ac6..46af0a151e0f6c014989a5cbb0dfa57a3410bbfa 100644
--- a/src/main/helm/templates/_helpers.tpl
+++ b/src/main/helm/templates/_helpers.tpl
@@ -24,6 +24,10 @@ app.kubernetes.io/namespace: {{ .Release.Namespace }}
 {{- end }}
 {{- end }}
 
+{{- define "app.nameToIdentifier" -}}
+{{- trimAll "-" ( regexReplaceAll "[^a-zA-Z0-9-]" (lower .) "" ) | trunc 20 }}
+{{- end -}}
+
 {{- define "app.envSpringProfiles" }}
 {{- if (.Values.env).overrideSpringProfiles -}}
 {{ printf "%s" (.Values.env).overrideSpringProfiles }}
diff --git a/src/main/helm/templates/keycloak-client-crd.yaml b/src/main/helm/templates/keycloak-client-crd.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..63c8232459bb4babbfe34daad62cdfef2a7ac45b
--- /dev/null
+++ b/src/main/helm/templates/keycloak-client-crd.yaml
@@ -0,0 +1,27 @@
+{{- if not (.Values.sso).disableOzgOperator -}}
+apiVersion: operator.ozgcloud.de/v1
+kind: OzgCloudKeycloakClient
+metadata:
+  name: administration-keycloak-client
+  namespace: {{ .Release.Namespace }}
+spec:
+  keep_after_delete: {{ (.Values.sso).keep_after_delete | default false }}
+  client_name: admin
+  client_base_url: https://{{ include "app.baseDomain" $ }}
+  client_web_origins:
+    - https://{{ include "app.baseDomain" $ }}
+  client_redirect_uris:
+    - https://{{ include "app.baseDomain" $ }}
+    - https://{{ include "app.baseDomain" $ }}/*
+  {{- if ((.Values.sso).client).additional_redirect_uris }}
+    {{- with ((.Values.sso).client).additional_redirect_uris }}
+{{ toYaml . | indent 4 }}
+    {{- end }}
+  {{- end }}
+  {{- if ((.Values.sso).client).client_roles }}
+  client_roles:
+    {{- with ((.Values.sso).client).client_roles }}
+{{ toYaml . | indent 4 }}
+    {{- end }}
+  {{- end }}
+{{- end -}}
\ No newline at end of file
diff --git a/src/main/helm/templates/keycloak_user_crd.yaml b/src/main/helm/templates/keycloak_user_crd.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..8dc497fa1fa9f4707f3ef8457e78db892d83ca42
--- /dev/null
+++ b/src/main/helm/templates/keycloak_user_crd.yaml
@@ -0,0 +1,38 @@
+{{- if not (.Values.sso).disableOzgOperator -}}
+{{ range $user := concat ((.Values.sso).api_users | default list) ((.Values.sso).keycloak_users | default list) }}
+---
+apiVersion: operator.ozgcloud.de/v1
+kind: OzgCloudKeycloakUser
+metadata:
+  name: {{ include "app.nameToIdentifier" $user.name }}-keycloak-user
+  namespace: {{ $.Release.Namespace }}
+spec:
+  keep_after_delete: {{ $.Values.sso.keep_after_delete | default false }}
+  update_user: {{ $user.update_user | default false }}
+  keycloak_user:
+    username: {{ $user.name | lower }}
+    first_name: {{ $user.first_name | default "" }}
+    last_name: {{ $user.last_name | default "" }}
+    email: {{ $user.email | default "" }}
+  {{- if $user.groups }}
+    groups: {{ $user.groups | toJson }}
+  {{- else }}
+    groups: [ ]
+  {{- end }}
+  {{- if $user.password }}
+    password: {{ $user.password }}
+  {{- end }}
+  {{- if $user.realm_roles }}
+    realm_roles:
+    {{- with $user.realm_roles }}
+{{ toYaml . | indent 6 }}
+    {{- end }}
+  {{- end }}
+  {{- if $user.client_roles }}
+    client_roles:
+    {{- with $user.client_roles }}
+{{ toYaml . | indent 6 }}
+    {{- end }}
+  {{- end }}
+{{ end }}
+{{- end -}}
\ No newline at end of file
diff --git a/src/main/helm/templates/ozgcloud_keycloak_operator_secrets_read_role.yaml b/src/main/helm/templates/ozgcloud_keycloak_operator_secrets_read_role.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..45514b61090d0c869bd29a6a352b58ad72dfb653
--- /dev/null
+++ b/src/main/helm/templates/ozgcloud_keycloak_operator_secrets_read_role.yaml
@@ -0,0 +1,21 @@
+{{- if not (.Values.sso).disableOzgOperator }}
+{{- if or ((.Values.sso).keycloak_users) ((.Values.sso).api_users) }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: ozgcloud-keycloak-operator-secrets-read-role-administration
+  namespace: {{ $.Release.Namespace }}
+rules:
+  - apiGroups:
+      - "*"
+    resourceNames:
+    {{ range $user := concat (.Values.sso.keycloak_users | default list) (.Values.sso.api_users | default list) }}
+      - {{ include "app.nameToIdentifier" $user.name }}-credentials
+    {{ end }}
+    resources:
+      - secrets
+    verbs:
+      - get
+      - list
+{{- end }}
+{{- end }}
\ No newline at end of file
diff --git a/src/main/helm/templates/ozgcloud_keycloak_operator_secrets_read_role_binding.yaml b/src/main/helm/templates/ozgcloud_keycloak_operator_secrets_read_role_binding.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..813ed63e3c3e2599026885e82c418b88f43d54b1
--- /dev/null
+++ b/src/main/helm/templates/ozgcloud_keycloak_operator_secrets_read_role_binding.yaml
@@ -0,0 +1,17 @@
+{{- if not (.Values.sso).disableOzgOperator }}
+{{- if or ((.Values.sso).keycloak_users) ((.Values.sso).api_users) }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: ozgcloud-keycloak-operator-secrets-read-role-binding-administration
+  namespace: {{ .Release.Namespace }}
+subjects:
+  - kind: ServiceAccount
+    name: ozgcloud-keycloak-operator-serviceaccount
+    namespace: {{ required "sso.operatorNamespace muss angegeben sein" .Values.sso.operatorNamespace }}
+roleRef:
+  kind: Role
+  name: ozgcloud-keycloak-operator-secrets-read-role-administration
+  apiGroup: rbac.authorization.k8s.io
+{{- end }}
+{{- end }}
\ No newline at end of file
diff --git a/src/main/helm/templates/ozgcloud_keycloak_operator_secrets_write_role.yaml b/src/main/helm/templates/ozgcloud_keycloak_operator_secrets_write_role.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..f8a609002bc284cd12ab42acdb97f096b77bd100
--- /dev/null
+++ b/src/main/helm/templates/ozgcloud_keycloak_operator_secrets_write_role.yaml
@@ -0,0 +1,16 @@
+{{- if not (.Values.sso).disableOzgOperator }}
+{{- if or ((.Values.sso).keycloak_users) ((.Values.sso).api_users) }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: ozgcloud-keycloak-operator-secrets-write-role-administration
+  namespace: {{ .Release.Namespace }}
+rules:
+  - apiGroups:
+      - "*"
+    resources:
+      - secrets
+    verbs:
+      - create
+{{- end }}
+{{- end }}
\ No newline at end of file
diff --git a/src/main/helm/templates/ozgcloud_keycloak_operator_secrets_write_role_binding.yaml b/src/main/helm/templates/ozgcloud_keycloak_operator_secrets_write_role_binding.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..2a1e624fbf66480ba9615ef3d7a939f3359d1418
--- /dev/null
+++ b/src/main/helm/templates/ozgcloud_keycloak_operator_secrets_write_role_binding.yaml
@@ -0,0 +1,17 @@
+{{- if not (.Values.sso).disableOzgOperator }}
+{{- if or ((.Values.sso).keycloak_users) ((.Values.sso).api_users) }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: ozgcloud-keycloak-operator-secrets-write-role-binding-administration
+  namespace: {{ .Release.Namespace }}
+subjects:
+  - kind: ServiceAccount
+    name: ozgcloud-keycloak-operator-serviceaccount
+    namespace: {{ required "sso.operatorNamespace muss angegeben sein" .Values.sso.operatorNamespace }}
+roleRef:
+  kind: Role
+  name: ozgcloud-keycloak-operator-secrets-write-role-administration
+  apiGroup: rbac.authorization.k8s.io
+{{- end }}
+{{- end }}
\ No newline at end of file
diff --git a/src/test/helm-linter-values.yaml b/src/test/helm-linter-values.yaml
index ad94c6407fb61577bf01debce438b4be572bc398..b58fe6625dc73d30769fb4ad0f4b397850ad07ab 100644
--- a/src/test/helm-linter-values.yaml
+++ b/src/test/helm-linter-values.yaml
@@ -32,4 +32,16 @@ networkPolicy:
   dnsServerNamespace: dummy-dns
 
 sso:
-  serverUrl: https://sso.company.local
\ No newline at end of file
+  serverUrl: https://sso.company.local
+  operatorNamespace: ozgcloud-keycloak-operator
+  api_users:
+    - name: dummy-user
+      first_name: dummy
+      last_name: user
+  client:
+    additional_redirect_uris:
+      - https://dummy:8080
+      - https://dummy:8080/*
+    client_roles:
+      - ROLE_DUMMY
+
diff --git a/src/test/helm/keycloak_client_crd_test.yaml b/src/test/helm/keycloak_client_crd_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..03f8d1c5d3e6822eb601c4d11b8205c5c1796e82
--- /dev/null
+++ b/src/test/helm/keycloak_client_crd_test.yaml
@@ -0,0 +1,106 @@
+#
+# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den
+# Ministerpräsidenten des Landes Schleswig-Holstein
+# Staatskanzlei
+# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+#
+# Lizenziert unter der EUPL, Version 1.2 oder - sobald
+# diese von der Europäischen Kommission genehmigt wurden -
+# Folgeversionen der EUPL ("Lizenz");
+# Sie dürfen dieses Werk ausschließlich gemäß
+# dieser Lizenz nutzen.
+# Eine Kopie der Lizenz finden Sie hier:
+#
+# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+#
+# Sofern nicht durch anwendbare Rechtsvorschriften
+# gefordert oder in schriftlicher Form vereinbart, wird
+# die unter der Lizenz verbreitete Software "so wie sie
+# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+# ausdrücklich oder stillschweigend - verbreitet.
+# Die sprachspezifischen Genehmigungen und Beschränkungen
+# unter der Lizenz sind dem Lizenztext zu entnehmen.
+#
+
+suite: Keycloak Client crd
+release:
+  name: administration
+  namespace: by-helm-test
+templates:
+  - templates/keycloak-client-crd.yaml
+set:
+  ozgcloud:
+    bundesland: by
+    bezeichner: helm
+    environment: test
+tests:
+  - it: should contain header data
+    asserts:
+      - isAPIVersion:
+          of: operator.ozgcloud.de/v1
+      - isKind:
+          of: OzgCloudKeycloakClient
+  - it: should have metadata
+    asserts:
+      - equal:
+          path: metadata.name
+          value: administration-keycloak-client
+      - equal:
+          path: metadata.namespace
+          value: by-helm-test
+  - it: should set keep after delete default to false
+    asserts:
+      - equal:
+          path: spec.keep_after_delete
+          value: false
+  - it: should use keep after delete value
+    set:
+      sso:
+        keep_after_delete: true
+    asserts:
+      - equal:
+          path: spec.keep_after_delete
+          value: true
+  - it: has client name value
+    asserts:
+      - equal:
+          path: spec.client_name
+          value: admin
+  - it: should set client base url
+    asserts:
+      - equal:
+          path: spec.client_base_url
+          value: https://helm-admin.ozg-sh.de
+  - it: should set client redirect uris
+    asserts:
+      - equal:
+          path: spec.client_web_origins
+          value:
+            - https://helm-admin.ozg-sh.de
+  - it: should set client web origins
+    asserts:
+      - equal:
+          path: spec.client_redirect_uris
+          value:
+            - https://helm-admin.ozg-sh.de
+            - https://helm-admin.ozg-sh.de/*
+  - it: should use additional redirect uris for client web origins
+    set:
+      sso:
+        client:
+          additional_redirect_uris:
+            - https://additional.url.de
+    asserts:
+      - equal:
+          path: spec.client_redirect_uris
+          value:
+            - https://helm-admin.ozg-sh.de
+            - https://helm-admin.ozg-sh.de/*
+            - https://additional.url.de
+  - it: should not create client cr if ozg operator is disabled
+    set:
+      sso:
+        disableOzgOperator: true
+    asserts:
+      - hasDocuments:
+          count: 0
\ No newline at end of file
diff --git a/src/test/helm/keycloak_user_crd_test.yaml b/src/test/helm/keycloak_user_crd_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..29c901d0387015bdb782a9ad0b71a115ea16c2cc
--- /dev/null
+++ b/src/test/helm/keycloak_user_crd_test.yaml
@@ -0,0 +1,565 @@
+#
+# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den
+# Ministerpräsidenten des Landes Schleswig-Holstein
+# Staatskanzlei
+# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+#
+# Lizenziert unter der EUPL, Version 1.2 oder - sobald
+# diese von der Europäischen Kommission genehmigt wurden -
+# Folgeversionen der EUPL ("Lizenz");
+# Sie dürfen dieses Werk ausschließlich gemäß
+# dieser Lizenz nutzen.
+# Eine Kopie der Lizenz finden Sie hier:
+#
+# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+#
+# Sofern nicht durch anwendbare Rechtsvorschriften
+# gefordert oder in schriftlicher Form vereinbart, wird
+# die unter der Lizenz verbreitete Software "so wie sie
+# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+# ausdrücklich oder stillschweigend - verbreitet.
+# Die sprachspezifischen Genehmigungen und Beschränkungen
+# unter der Lizenz sind dem Lizenztext zu entnehmen.
+#
+
+suite: Keycloak User crd
+release:
+  name: administration
+  namespace: by-helm-test
+templates:
+  - templates/keycloak_user_crd.yaml
+tests:
+  - it: should contain header data
+    set:
+      sso:
+        api_users:
+          - name: testapiuser
+    asserts:
+      - isAPIVersion:
+          of: operator.ozgcloud.de/v1
+      - isKind:
+          of: OzgCloudKeycloakUser
+  - it: should have metadata
+    set:
+      sso:
+        api_users:
+          - name: testapiuser
+    asserts:
+      - equal:
+          path: metadata.name
+          value: testapiuser-keycloak-user
+      - equal:
+          path: metadata.namespace
+          value: by-helm-test
+  - it: should have default Values
+    set:
+      ozgcloud:
+        bundesland: by
+        bezeichner: helm
+        environment: test
+      sso:
+        api_users:
+          - name: testapiuser
+    asserts:
+      - equal:
+          path: spec.keep_after_delete
+          value: false
+      - equal:
+          path: spec.keycloak_user.first_name
+          values: ""
+      - equal:
+          path: spec.keycloak_user.last_name
+          values: ""
+      - equal:
+          path: spec.keycloak_user.email
+          values: ""
+      - isEmpty:
+          path: spec.keycloak_user.groups
+      - isNull:
+          path: spec.keycloak_user.password
+      - isNull:
+          path: spec.keycloak_user.realm_roles
+      - isNull:
+          path: spec.keycloak_user.client_roles
+
+  - it: should have Keycloak User without roles
+    set:
+      ozgcloud:
+        bundesland: by
+        bezeichner: helm
+        environment: test
+      baseUrl: "test.by.ozg-cloud.de"
+      sso:
+        keycloak_users:
+          - name: dorothea
+            first_name: Dorothea
+            last_name: Doe
+            email: dorothea@ozg-sh.de
+    asserts:
+      - equal:
+          path: spec.keep_after_delete
+          value: false
+      - equal:
+          path: spec.keycloak_user.username
+          value: dorothea
+      - equal:
+          path: spec.keycloak_user.first_name
+          value: Dorothea
+      - equal:
+          path: spec.keycloak_user.last_name
+          value: Doe
+      - equal:
+          path: spec.keycloak_user.email
+          value: dorothea@ozg-sh.de
+      - isEmpty:
+          path: spec.keycloak_user.groups
+      - isNull:
+          path: spec.keycloak_user.realm_roles
+      - isNull:
+          path: spec.keycloak_user.client_roles
+
+  - it: should have Keycloak User with client role
+    set:
+      ozgcloud:
+        bundesland: by
+        bezeichner: helm
+        environment: test
+      baseUrl: "test.by.ozg-cloud.de"
+      sso:
+        keycloak_users:
+          - name: dorothea
+            first_name: Dorothea
+            last_name: Doe
+            email: dorothea@ozg-sh.de
+            client_roles:
+              - name: administration
+                role: ROLE_A
+    asserts:
+      - equal:
+          path: spec.keep_after_delete
+          value: false
+      - equal:
+          path: spec.keycloak_user.username
+          value: dorothea
+      - equal:
+          path: spec.keycloak_user.first_name
+          value: Dorothea
+      - equal:
+          path: spec.keycloak_user.last_name
+          value: Doe
+      - equal:
+          path: spec.keycloak_user.email
+          value: dorothea@ozg-sh.de
+      - isEmpty:
+          path: spec.keycloak_user.groups
+      - isNull:
+          path: spec.keycloak_user.realm_roles
+      - contains:
+          path: spec.keycloak_user.client_roles
+          content:
+            name: administration
+            role: ROLE_A
+
+  - it: should have Keycloak User with realm role
+    set:
+      ozgcloud:
+        bundesland: by
+        bezeichner: helm
+        environment: test
+      baseUrl: "test.by.ozg-cloud.de"
+      sso:
+        keycloak_users:
+          - name: dorothea
+            first_name: Dorothea
+            last_name: Doe
+            email: dorothea@ozg-sh.de
+            realm_roles:
+              - "offline_access"
+    asserts:
+      - equal:
+          path: spec.keep_after_delete
+          value: false
+      - equal:
+          path: spec.keycloak_user.username
+          value: dorothea
+      - equal:
+          path: spec.keycloak_user.first_name
+          value: Dorothea
+      - equal:
+          path: spec.keycloak_user.last_name
+          value: Doe
+      - equal:
+          path: spec.keycloak_user.email
+          value: dorothea@ozg-sh.de
+      - isEmpty:
+          path: spec.keycloak_user.groups
+      - isNull:
+          path: spec.keycloak_user.client_roles
+      - contains:
+          path: spec.keycloak_user.realm_roles
+          content:
+            offline_access
+
+  - it: should have Keycloak User with group
+    set:
+      ozgcloud:
+        bundesland: by
+        bezeichner: helm
+        environment: test
+      baseUrl: "test.by.ozg-cloud.de"
+      sso:
+        keycloak_users:
+          - name: dorothea
+            first_name: Dorothea
+            last_name: Doe
+            email: dorothea@ozg-sh.de
+            groups:
+              - Bauamt
+    asserts:
+      - equal:
+          path: spec.keep_after_delete
+          value: false
+      - equal:
+          path: spec.keycloak_user.username
+          value: dorothea
+      - equal:
+          path: spec.keycloak_user.first_name
+          value: Dorothea
+      - equal:
+          path: spec.keycloak_user.last_name
+          value: Doe
+      - equal:
+          path: spec.keycloak_user.email
+          value: dorothea@ozg-sh.de
+      - isNull:
+          path: spec.keycloak_user.realm_roles
+      - isNull:
+          path: spec.keycloak_user.client_roles
+      - contains:
+          path: spec.keycloak_user.groups
+          content:
+            Bauamt
+
+  - it: should have Keycloak API User without roles
+    set:
+      ozgcloud:
+        bundesland: by
+        bezeichner: helm
+        environment: test
+      baseUrl: "test.by.ozg-cloud.de"
+      sso:
+        api_users:
+          - name: testapiuser
+            first_name: Api
+            last_name: User
+            email: testapiuser@ozg-sh.de
+    asserts:
+      - equal:
+          path: spec.keep_after_delete
+          value: false
+      - equal:
+          path: spec.keycloak_user.username
+          value: testapiuser
+      - equal:
+          path: spec.keycloak_user.first_name
+          value: Api
+      - equal:
+          path: spec.keycloak_user.last_name
+          value: User
+      - equal:
+          path: spec.keycloak_user.email
+          value: testapiuser@ozg-sh.de
+      - isEmpty:
+          path: spec.keycloak_user.groups
+      - isNull:
+          path: spec.keycloak_user.realm_roles
+      - isNull:
+          path: spec.keycloak_user.client_roles
+
+  - it: should have Keycloak API User with client role
+    set:
+      ozgcloud:
+        bundesland: by
+        bezeichner: helm
+        environment: test
+      baseUrl: "test.by.ozg-cloud.de"
+      sso:
+        api_users:
+          - name: testapiuser
+            first_name: Api
+            last_name: User
+            email: testapiuser@ozg-sh.de
+            client_roles:
+              - name: realm-management
+                role: view-users
+    asserts:
+      - equal:
+          path: spec.keep_after_delete
+          value: false
+      - equal:
+          path: spec.keycloak_user.username
+          value: testapiuser
+      - equal:
+          path: spec.keycloak_user.first_name
+          value: Api
+      - equal:
+          path: spec.keycloak_user.last_name
+          value: User
+      - equal:
+          path: spec.keycloak_user.email
+          value: testapiuser@ozg-sh.de
+      - isEmpty:
+          path: spec.keycloak_user.groups
+      - isNull:
+          path: spec.keycloak_user.realm_roles
+      - contains:
+          path: spec.keycloak_user.client_roles
+          content:
+            name: realm-management
+            role: view-users
+
+  - it: should have Keycloak API User with realm role
+    set:
+      ozgcloud:
+        bundesland: by
+        bezeichner: helm
+        environment: test
+      baseUrl: "test.by.ozg-cloud.de"
+      sso:
+        api_users:
+          - name: testapiuser
+            first_name: Api
+            last_name: User
+            email: testapiuser@ozg-sh.de
+            realm_roles:
+              - "offline_access"
+    asserts:
+      - equal:
+          path: spec.keep_after_delete
+          value: false
+      - equal:
+          path: spec.keycloak_user.username
+          value: testapiuser
+      - equal:
+          path: spec.keycloak_user.first_name
+          value: Api
+      - equal:
+          path: spec.keycloak_user.last_name
+          value: User
+      - equal:
+          path: spec.keycloak_user.email
+          value: testapiuser@ozg-sh.de
+      - isEmpty:
+          path: spec.keycloak_user.groups
+      - isNull:
+          path: spec.keycloak_user.client_roles
+      - contains:
+          path: spec.keycloak_user.realm_roles
+          content:
+            offline_access
+
+  - it: should have Keycloak API User with group
+    set:
+      ozgcloud:
+        bundesland: by
+        bezeichner: helm
+        environment: test
+      baseUrl: "test.by.ozg-cloud.de"
+      sso:
+        api_users:
+          - name: testapiuser
+            first_name: Api
+            last_name: User
+            email: testapiuser@ozg-sh.de
+            groups:
+              - Bauamt
+    asserts:
+      - equal:
+          path: spec.keep_after_delete
+          value: false
+      - equal:
+          path: spec.keycloak_user.username
+          value: testapiuser
+      - equal:
+          path: spec.keycloak_user.first_name
+          value: Api
+      - equal:
+          path: spec.keycloak_user.last_name
+          value: User
+      - equal:
+          path: spec.keycloak_user.email
+          value: testapiuser@ozg-sh.de
+      - isNull:
+          path: spec.keycloak_user.realm_roles
+      - isNull:
+          path: spec.keycloak_user.client_roles
+      - contains:
+          path: spec.keycloak_user.groups
+          content:
+            Bauamt
+
+  - it: should have Keycloak User and Keycloak API User
+    set:
+      ozgcloud:
+        bundesland: by
+        bezeichner: helm
+        environment: test
+      baseUrl: "test.by.ozg-cloud.de"
+      sso:
+        api_users:
+          - name: testapiuser
+            first_name: Api
+            last_name: User
+            email: testapiuser@ozg-sh.de
+            realm_roles:
+              - "offline_access"
+            client_roles:
+              - name: realm-management
+                role: view-users
+        keycloak_users:
+          - name: dorothea
+            first_name: Dorothea
+            last_name: Doe
+            email: dorothea@ozg-sh.de
+            realm_roles:
+              - "offline_access"
+            client_roles:
+              - name: administration
+                role: ROLE_A
+            groups:
+              - GroupA
+    asserts:
+      - hasDocuments:
+          count: 2
+      - equal:
+          path: spec.keep_after_delete
+          value: false
+        documentIndex: 0
+      - equal:
+          path: spec.keycloak_user.username
+          value: testapiuser
+        documentIndex: 0
+      - equal:
+          path: spec.keycloak_user.first_name
+          value: Api
+        documentIndex: 0
+      - equal:
+          path: spec.keycloak_user.last_name
+          value: User
+        documentIndex: 0
+      - equal:
+          path: spec.keycloak_user.email
+          value: testapiuser@ozg-sh.de
+        documentIndex: 0
+      - contains:
+          path: spec.keycloak_user.client_roles
+          content:
+            name: realm-management
+            role: view-users
+        documentIndex: 0
+      - contains:
+          path: spec.keycloak_user.realm_roles
+          content:
+            offline_access
+        documentIndex: 0
+      - equal:
+          path: spec.keep_after_delete
+          value: false
+        documentIndex: 1
+      - equal:
+          path: spec.keycloak_user.username
+          value: dorothea
+        documentIndex: 1
+      - equal:
+          path: spec.keycloak_user.first_name
+          value: Dorothea
+        documentIndex: 1
+      - equal:
+          path: spec.keycloak_user.last_name
+          value: Doe
+        documentIndex: 1
+      - equal:
+          path: spec.keycloak_user.email
+          value: dorothea@ozg-sh.de
+        documentIndex: 1
+      - contains:
+          path: spec.keycloak_user.groups
+          content:
+            GroupA
+        documentIndex: 1
+      - contains:
+          path: spec.keycloak_user.client_roles
+          content:
+            name: administration
+            role: ROLE_A
+        documentIndex: 1
+      - contains:
+          path: spec.keycloak_user.realm_roles
+          content:
+            offline_access
+        documentIndex: 1
+
+  - it: should have lowercase username
+    set:
+      ozgcloud:
+        bundesland: by
+        bezeichner: helm
+        environment: test
+      sso:
+        api_users:
+          - name: testApiUser
+    asserts:
+      - equal:
+          path: spec.keycloak_user.username
+          value: testapiuser
+      - equal:
+          path: spec.keep_after_delete
+          value: false
+      - equal:
+          path: spec.keycloak_user.first_name
+          values: ""
+      - equal:
+          path: spec.keycloak_user.last_name
+          values: ""
+      - equal:
+          path: spec.keycloak_user.email
+          values: ""
+      - isEmpty:
+          path: spec.keycloak_user.groups
+      - isNull:
+          path: spec.keycloak_user.password
+      - isNull:
+          path: spec.keycloak_user.realm_roles
+      - isNull:
+          path: spec.keycloak_user.client_roles
+
+  - it: should not create user cr if ozg operator is disabled
+    set:
+      sso:
+        disableOzgOperator: true
+        keycloak_users:
+          - name: kop
+    asserts:
+      - hasDocuments:
+          count: 0
+
+  - it: should set updateUser
+    set:
+      sso:
+        api_users:
+          - name: testapiuser
+            update_user: true
+    asserts:
+      - equal:
+          path: spec.update_user
+          value: true
+
+  - it: should set default updateUser to false
+    set:
+      sso:
+        api_users:
+          - name: testapiuser
+    asserts:
+      - equal:
+          path: spec.update_user
+          value: false
\ No newline at end of file
diff --git a/src/test/helm/ozgcloud_keycloak_operator_secrets_read_role_binding_test.yaml b/src/test/helm/ozgcloud_keycloak_operator_secrets_read_role_binding_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..6365584bef0d88e2cd172076609c1d918ae877ee
--- /dev/null
+++ b/src/test/helm/ozgcloud_keycloak_operator_secrets_read_role_binding_test.yaml
@@ -0,0 +1,87 @@
+suite: test ozg_operator_secrets_read_role_binding
+release:
+  name: administration
+  namespace: by-helm-test
+templates:
+  - templates/ozgcloud_keycloak_operator_secrets_read_role_binding.yaml
+tests:
+  - it: should contain header data
+    set:
+      sso:
+        keycloak_users:
+          - name: sabine
+        operatorNamespace: test-operator-namespace
+    asserts:
+      - isAPIVersion:
+          of: rbac.authorization.k8s.io/v1
+      - isKind:
+          of: RoleBinding
+  - it: should have metadata
+    set:
+      sso:
+        keycloak_users:
+          - name: sabine
+        operatorNamespace: test-operator-namespace
+    asserts:
+      - equal:
+          path: metadata.name
+          value: ozgcloud-keycloak-operator-secrets-read-role-binding-administration
+      - equal:
+          path: metadata.namespace
+          value: by-helm-test
+  - it: should have subjects values
+    set:
+      sso:
+        keycloak_users:
+          - name: sabine
+        operatorNamespace: test-operator-namespace
+    asserts:
+      - contains:
+          path: subjects
+          content:
+            kind: ServiceAccount
+            name: ozgcloud-keycloak-operator-serviceaccount
+            namespace: test-operator-namespace
+  - it: should have roleRef values
+    set:
+      sso:
+        keycloak_users:
+          - name: sabine
+        operatorNamespace: test-operator-namespace
+    asserts:
+      - equal:
+          path: roleRef.kind
+          value: Role
+      - equal:
+          path: roleRef.name
+          value: ozgcloud-keycloak-operator-secrets-read-role-administration
+      - equal:
+          path: roleRef.apiGroup
+          value: rbac.authorization.k8s.io
+  - it: should not create RoleBinding if no keycloak users available
+    asserts:
+      - hasDocuments:
+          count: 0
+  - it: should have subjects values on api_users
+    set:
+      sso:
+        api_users:
+          - name: apiUser
+        operatorNamespace: test-operator-namespace
+    asserts:
+      - contains:
+          path: subjects
+          content:
+            kind: ServiceAccount
+            name: ozgcloud-keycloak-operator-serviceaccount
+            namespace: test-operator-namespace
+
+  - it: should not create RoleBinding if ozg operator is disabled
+    set:
+      sso:
+        disableOzgOperator: true
+        api_users:
+          - name: apiUser
+    asserts:
+      - hasDocuments:
+          count: 0
diff --git a/src/test/helm/ozgcloud_keycloak_operator_secrets_read_role_test.yaml b/src/test/helm/ozgcloud_keycloak_operator_secrets_read_role_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..f5e9ad5adcec2bd2d26ac27d602acbd2e1f54c7d
--- /dev/null
+++ b/src/test/helm/ozgcloud_keycloak_operator_secrets_read_role_test.yaml
@@ -0,0 +1,85 @@
+suite: test ozg_operator_secrets_read_role
+release:
+  name: administration
+  namespace: by-helm-test
+templates:
+  - templates/ozgcloud_keycloak_operator_secrets_read_role.yaml
+tests:
+  - it: should contain header data
+    set:
+      sso:
+        keycloak_users:
+          - name: sabine
+    asserts:
+      - isAPIVersion:
+          of: rbac.authorization.k8s.io/v1
+      - isKind:
+          of: Role
+  - it: should have metadata
+    set:
+      sso:
+        keycloak_users:
+          - name: sabine
+    asserts:
+      - equal:
+          path: metadata.name
+          value: ozgcloud-keycloak-operator-secrets-read-role-administration
+      - equal:
+          path: metadata.namespace
+          value: by-helm-test
+  - it: should have subjects values
+    set:
+      sso:
+        keycloak_users:
+          - name: peter
+          - name: sabine
+          - name: _with_underscore_
+          - name: .with.dot.
+    asserts:
+      - contains:
+          path: rules
+          content:
+            apiGroups:
+              - "*"
+            resourceNames:
+              - peter-credentials
+              - sabine-credentials
+              - withunderscore-credentials
+              - withdot-credentials
+            resources:
+              - secrets
+            verbs:
+              - get
+              - list
+  - it: should not create RoleBinding if no keycloak users available
+    asserts:
+      - hasDocuments:
+          count: 0
+  - it: should have subjects values on api_users
+    set:
+      sso:
+        api_users:
+          - name: apiUser-
+    asserts:
+      - contains:
+          path: rules
+          content:
+            apiGroups:
+              - "*"
+            resourceNames:
+              - apiuser-credentials
+            resources:
+              - secrets
+            verbs:
+              - get
+              - list
+
+  - it: should not create Role if ozg operator is disabled
+    set:
+      sso:
+        disableOzgOperator: true
+        api_users:
+          - name: apiUser
+    asserts:
+      - hasDocuments:
+          count: 0
\ No newline at end of file
diff --git a/src/test/helm/ozgcloud_keycloak_operator_secrets_write_role_binding_test.yaml b/src/test/helm/ozgcloud_keycloak_operator_secrets_write_role_binding_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..cbc773bf876afcb77f766dd68ead204453be20e4
--- /dev/null
+++ b/src/test/helm/ozgcloud_keycloak_operator_secrets_write_role_binding_test.yaml
@@ -0,0 +1,87 @@
+suite: test ozg_operator_secrets_role_binding
+release:
+  name: administration
+  namespace: by-helm-test
+templates:
+  - templates/ozgcloud_keycloak_operator_secrets_write_role_binding.yaml
+tests:
+  - it: should contain header data
+    set:
+      sso:
+        keycloak_users:
+          - name: sabine
+        operatorNamespace: test-operator-namespace
+    asserts:
+      - isAPIVersion:
+          of: rbac.authorization.k8s.io/v1
+      - isKind:
+          of: RoleBinding
+  - it: should have metadata
+    set:
+      sso:
+        keycloak_users:
+          - name: sabine
+        operatorNamespace: test-operator-namespace
+    asserts:
+      - equal:
+          path: metadata.name
+          value: ozgcloud-keycloak-operator-secrets-write-role-binding-administration
+      - equal:
+          path: metadata.namespace
+          value: by-helm-test
+  - it: should have subjects values
+    set:
+      sso:
+        keycloak_users:
+          - name: sabine
+        operatorNamespace: test-operator-namespace
+    asserts:
+      - contains:
+          path: subjects
+          content:
+            kind: ServiceAccount
+            name: ozgcloud-keycloak-operator-serviceaccount
+            namespace: test-operator-namespace
+  - it: should have roleRef values
+    set:
+      sso:
+        keycloak_users:
+          - name: sabine
+        operatorNamespace: test-operator-namespace
+    asserts:
+      - equal:
+          path: roleRef.kind
+          value: Role
+      - equal:
+          path: roleRef.name
+          value: ozgcloud-keycloak-operator-secrets-write-role-administration
+      - equal:
+          path: roleRef.apiGroup
+          value: rbac.authorization.k8s.io
+  - it: should not create RoleBinding if no keycloak users available
+    asserts:
+      - hasDocuments:
+          count: 0
+  - it: should have subjects values on api_users
+    set:
+      sso:
+        api_users:
+          - name: apiUsers
+        operatorNamespace: test-operator-namespace
+    asserts:
+      - contains:
+          path: subjects
+          content:
+            kind: ServiceAccount
+            name: ozgcloud-keycloak-operator-serviceaccount
+            namespace: test-operator-namespace
+
+  - it: should not create RoleBinding if ozg operator is disabled
+    set:
+      sso:
+        disableOzgOperator: true
+        api_users:
+          - name: apiUser
+    asserts:
+      - hasDocuments:
+          count: 0
diff --git a/src/test/helm/ozgcloud_keycloak_operator_secrets_write_role_test.yaml b/src/test/helm/ozgcloud_keycloak_operator_secrets_write_role_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..a06970b1d97c3800f49474ce22173467547a0703
--- /dev/null
+++ b/src/test/helm/ozgcloud_keycloak_operator_secrets_write_role_test.yaml
@@ -0,0 +1,74 @@
+suite: test ozg_operator_secrets_write_role
+release:
+  name: administration
+  namespace: by-helm-test
+templates:
+  - templates/ozgcloud_keycloak_operator_secrets_write_role.yaml
+tests:
+  - it: should contain header data
+    set:
+      sso:
+        keycloak_users:
+          - name: sabine
+    asserts:
+      - isAPIVersion:
+          of: rbac.authorization.k8s.io/v1
+      - isKind:
+          of: Role
+  - it: should have metadata
+    set:
+      sso:
+        keycloak_users:
+          - name: sabine
+    asserts:
+      - equal:
+          path: metadata.name
+          value: ozgcloud-keycloak-operator-secrets-write-role-administration
+      - equal:
+          path: metadata.namespace
+          value: by-helm-test
+  - it: should have subjects values
+    set:
+      sso:
+        keycloak_users:
+          - name: peter
+          - name: sabine
+    asserts:
+      - contains:
+          path: rules
+          content:
+            apiGroups:
+              - "*"
+            resources:
+              - secrets
+            verbs:
+              - create
+  - it: should not create RoleBinding if no keycloak users available
+    asserts:
+      - hasDocuments:
+          count: 0
+  - it: should have subjects values on api_users
+    set:
+      sso:
+        api_users:
+          - name: apiUser
+    asserts:
+      - contains:
+          path: rules
+          content:
+            apiGroups:
+              - "*"
+            resources:
+              - secrets
+            verbs:
+              - create
+
+  - it: should not create Role if ozg operator is disabled
+    set:
+      sso:
+        disableOzgOperator: true
+        api_users:
+          - name: apiUser
+    asserts:
+      - hasDocuments:
+          count: 0
\ No newline at end of file