Tech Tip – Formatting External Secrets in Helm

This has tripped me up a lot, so I figure it is worth a quick note.

The Problem

I use Helm charts to define the state of my cluster in a Git repository, and ArgoCD to deploy those charts. This allows a lot of flexibility in my deployments and configuration.

For secrets management, I use External Secrets to populate secrets from Hashicorp Vault. In many of those cases, I need to use the templating functionality of External Secrets to build secrets that can be used from external charts. A great case of this is populating user secrets for the RabbitMQ chart.

In the link above, you will notice the templates/default-user-secrets.yaml file. This file is meant to generate a Kubernetes Secret resource which is then sent to the RabbitMqCluster resource (templates/cluster.yaml). This secret is mounted as a file, and therefore, needs some custom formatting. So I used the template property to format the secret:

  type: Opaque
  engineVersion: v2
    default_user.conf: |
        default_user={{ `{{ .username  }}` }}
        default_pass={{ `{{ .password  }}` }}
    host: {{ .Release.Name }}.rabbitmq.svc
    password: {{`"{{ .password }}"`}}
    port: "5672"
    provider: rabbitmq
    type: rabbitmq
    username: {{`"{{ .username }}"`}}

Notice in the code above the duplicated {{ and }} around the username/password values. These are necessary to ensure that the template is properly set in the ExternalSecret resource.

But, Why?

It has to do with templating. Helm uses golang templates to process the templates and create resources. Similarly, the ExternalSecrets template engine uses golang templates. When you have a “template in a template”, you have to somehow tell the processor to put the literal value in.

Let’s look at one part of this file.

  default_user={{ `{{ .username  }}` }}

What we want to end up in the ExternalSecret template is this:

default_user={{ .username  }}

So, in order to do that, we have to tell the Helm template to write {{ .username }} as written, not processing it as a golang template. In this case, we use the backtick (`) to allow for this escape without having that value written to the template. Notice that other areas use the double-quote (“) to wrap the template.

password: {{`"{{ .password }}"`}}

This will generate the quotes in the resulting template:

password: "{{ .password }}"

If you need a single quote, the use the same pattern, but replace the double quote with a single quote (‘).

username: {{`'{{ .username }}'`}}

For whatever it is worth, VS Code’s YAML parser did not like that version at all. Since I have not run into a situation where I need a single quote, I use double quotes if quotes are required, and backticks if they are not.