Skip to content

Targeting Custom Resources

Maturity

At the time of this writing (1.11.2025) this feature is in heavy alpha status. Please consider the following documentation with the limitations and guardrails described below.

External Secrets Operator can create and manage resources beyond Kubernetes Secrets. When you need to populate ConfigMaps or Custom Resource Definitions with secret data from your external provider, you can use the manifest target feature.

Security Consideration

Custom resources are not encrypted at rest by Kubernetes. Only use this feature when you need to populate resources that do not contain sensitive credentials, or when the target resource is encrypted by other means.

This feature must be explicitly enabled in your deployment using the --unsafe-allow-non-secret-targets flag.

Namespaced Resources Only

With this feature you can only target namespaced resources - and resources can only be managed by an ExternalSecret in the same namespace as the resource.

Performance

Using generic targets or custom resources at the moment of this writing is ~20% slower than handling secrets due to certain missing features yet to be implemented. We recommend not overusing this feature without too many objects until further performance improvement are implemented.

Basic ConfigMap Example

The simplest use case is creating a ConfigMap from external secrets. This is useful when applications expect configuration in ConfigMaps rather than Secrets, or when the data is not sensitive.

apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: application-config
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: app-config
    manifest:
      apiVersion: v1
      kind: ConfigMap
  data:
  - secretKey: database-host
    remoteRef:
      key: config/database/host
  - secretKey: api-endpoint
    remoteRef:
      key: config/api/endpoint

This creates a ConfigMap named app-config with the data populated from your secret provider.

Custom Resource Definitions

You can target any custom resource that exists in your cluster. This example creates an Argo CD Application resource:

apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: argocd-app
spec:
  refreshInterval: 15m
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: my-application
    manifest:
      apiVersion: argoproj.io/v1alpha1
      kind: Application
  dataFrom:
  - extract:
      key: argocd/applications/my-app

The operator will create or update the Application resource with the data from your external secret provider.

Templating with Custom Resources

Templates work with custom resources just as they do with Secrets. You can use the template.data field to create structured configuration:

apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: templated-config
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: database-config
    manifest:
      apiVersion: v1
      kind: ConfigMap
    template:
      engineVersion: v2
      data:
        database.yaml: |
          host: {{ .dbHost }}
          port: 5432
          database: {{ .dbName }}
          connection_string: "postgresql://user:{{ .dbPassword }}@{{ .dbHost }}:5432/{{ .dbName }}"
  data:
  - secretKey: dbHost
    remoteRef:
      key: database/hostname
  - secretKey: dbName
    remoteRef:
      key: database/name
  - secretKey: dbPassword
    remoteRef:
      key: database/password

Advanced Path Targeting

When working with custom resources that have complex structures, you can use target to specify where template output should be placed. This is particularly useful for resources with nested specifications.

apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: custom-resource-config
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: app-settings
    manifest:
      apiVersion: config.example.com/v1
      kind: AppConfig
    template:
      engineVersion: v2
      templateFrom:
      - literal: |
          host: {{ .dbHost }}
          port: {{ .dbPort }}
          credentials:
            username: {{ .dbUser }}
            password: {{ .dbPassword }}
        target: spec.database
      - literal: |
          level: info
          format: json
        target: spec.logging
  data:
  - secretKey: dbHost
    remoteRef:
      key: database/host
  - secretKey: dbPort
    remoteRef:
      key: database/port
  - secretKey: dbUser
    remoteRef:
      key: database/username
  - secretKey: dbPassword
    remoteRef:
      key: database/password

The target field accepts dot-notation paths like spec.database or spec.logging to place the rendered template output at specific locations in the resource structure. When target is not specified it defaults to Data for backward compatibility with Secrets.

Drift Detection

The operator automatically detects and corrects manual changes to managed custom resources. If you modify a ConfigMap or custom resource that is managed by an ExternalSecret, the operator will restore it to the desired state during the next reconciliation cycle.

This ensures that your configuration remains consistent with what is defined in your external secret provider, preventing configuration drift.

Metadata and Labels

You can add labels and annotations to your target resources using the template metadata:

apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: labeled-config
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: labeled-configmap
    manifest:
      apiVersion: v1
      kind: ConfigMap
    template:
      engineVersion: v2
      metadata:
        labels:
          app: myapp
          environment: production
        annotations:
          description: "Managed by External Secrets Operator"
      data:
        config.json: |
          {
            "feature_flags": {
              "new_ui": {{ .featureNewUI }},
              "beta_api": {{ .featureBetaAPI }}
            }
          }
  data:
  - secretKey: featureNewUI
    remoteRef:
      key: features/new-ui
  - secretKey: featureBetaAPI
    remoteRef:
      key: features/beta-api

The operator automatically adds the externalsecrets.external-secrets.io/managed: "true" label to track which resources it manages.

RBAC Requirements

When using custom resource targets, ensure the External Secrets Operator has appropriate RBAC permissions to create and manage those resources. The Helm chart provides configuration options to enable these permissions:

nonSecretTargets:
  enabled: true
  rbac:
    configMaps: true
    customResources:
    - apiGroups: ["config.example.com"]
      resources: ["appconfigs"]

Without these permissions, the operator will not be able to create or update your target resources.