Generating values in Kubernetes secrets
Helm templates can generate values that can be used in Kubernetes secrets. A Helm template for a Kubernetes secret which generates a password if a value isn’t defined in the Helm values looks something like this:
apiVersion: v1
kind: Secret
metadata:
name: postgres-credentials
type: Opaque
data:
postgresPassword: {{ default (randAlphaNum 64) .Values.postgresPassword.value | b64enc | quote }}
Using the lookup function
What if we only wanted Helm to generate a new value in the secret if there was no existing Kubernetes secret containing a key from which we wanted to reuse the value?
As an example, our application needs to connect to a Postgres instance. Our Helm chart creates a Kubernetes secret containing a generated password and a Kubernetes job which creates a user on our Postgres instance using our new password if a Postgres user for our application does not already exist. If we reinstalled our Helm chart, Helm would generate a new Kubernetes secret and password, resulting in our application using incorrect Postgres credentials.
This problem can be solved using Helm’s lookup function. The lookup function queries live Kubernetes resources from the cluster. It takes four arguments: API version, kind, namespace, and name.
{{ lookup "v1" "Secret" .Release.Namespace "postgres-credentials" }}
This will retrieve the postgres-credentials secret from the release namespace. If the resource does not exist, the lookup function will return an empty map. Note that lookup always returns empty data during helm template or helm diff, since there is no cluster to query.
We can use the lookup function’s output at a specific index in our template.
{{ $secret := (lookup "v1" "Secret" .Release.Namespace $secretName) }}
apiVersion: v1
kind: Secret
metadata:
name: postgres-credentials
type: Opaque
data:
postgresPassword: {{ index $secret.data "postgresPassword" }}
Putting it all together
Combining those two approaches, what if we wanted to create a template for a Kubernetes secret containing a key, the value defined in the Helm values file? If the value in the values file is empty, it reuses the value from an existing secret. If there is no existing secret, the template will use a newly generated value.
The way to achieve this is first to generate a new variable $postgresPassword, which has a value of .Values.postgresPassword.value, and defaults to generating a new random string using randAlphaNum64.
{{- $postgresPassword := default (randAlphaNum 64) .Values.postgresPassword.value | b64enc | quote }}
We use an if statement to check whether .Values.postgresPassword has a value. If .Values.postgresPassword is does not have a value; we use the lookup function to retrieve an existing Kubernetes secret, postgres-credentials.
{{- if not .Values.postgresPassword.value }}
{{- $existingSecret := (lookup "v1" "Secret" .Release.Namespace "postgres-credentials") }}
We check whether the lookup function found an existing secret and, if it did, we set $postgresPassword to the value output by the lookup function at index "postgresPassword". We then close both if statements.
{{- if $existingSecret }}
{{- $postgresPassword = index $existingSecret.data "postgresPassword"}}
{{- end -}}
{{- end -}}
We ensure that this secret is persisted so we can copy its value, even if we uninstall the Helm deployment. We do this using the Helm resource policy annotation:
annotations:
"helm.sh/resource-policy": "keep"
Finally, we set the value of the postgresPassword key in our secret to $postgresPassword:
data:
postgresPassword: {{ $postgresPassword }}
Putting this all together gives us the following template:
{{- $postgresPassword := default (randAlphaNum 64) .Values.postgresPassword.value | b64enc | quote }}
{{- if not .Values.postgresPassword.value }}
{{- $existingSecret := (lookup "v1" "Secret" .Release.Namespace "postgres-credentials") }}
{{- if $existingSecret }}
{{- $postgresPassword = index $existingSecret.data "postgresPassword"}}
{{- end -}}
{{- end -}}
apiVersion: v1
kind: Secret
metadata:
name: postgres-credentials
annotations:
"helm.sh/resource-policy": "keep"
type: Opaque
data:
postgresPassword: {{ $postgresPassword }}

