Reusing Existing Kubernetes Secrets in Helm Templates
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. To use the lookup
function in Helm, you can include it in a template.
{{ lookup .Values "key" }}
This lookup
function will retrieve the value of the key key
from the Values
object. If the key does not exist or the Values
object is not a map or an object, the lookup
function will return an empty string.
You can also use the lookup
function to retrieve values from a Kubernetes resource, such as a secret
, by specifying the resource API version, type
, namespace
, and name
.
{{ lookup "v1" "Secret" .Release.Namespace "postgres-credentials" }}
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 }}