```yaml
---
title: Sync to Custom Resources
version: v1alpha1
authors: Gustavo Carvalho
creation-date: 2024-05-03
status: approved
---
```

# Sync to Custom Resources Design

## Table of Contents

<!-- toc -->
// autogen please
<!-- /toc -->

## Summary

This design document describes how `ExternalSecrets` can leverage templates to generate non-kubernetes `Secret` resource as the target. This allows to push Sensitive information to specific CRs, `ConfiMaps`, etc.

## Motivation

Currently, several "semi-sensitive" information needs to be provisioned directly into CRs and ConfigMaps (such as OIDC Client IDs). While these information are not strictly speaking a secret, several regulated environments must treat them as such, causing several operational overhead to deal with this information - specially on a GitOps setup.

## Proposal

To simplify the workflow and enhance user experience, the proposal is to integrate a functionality to template the whole manifest. this would be additional logic to the existing `target: Manifest` directly into the `templateFrom.[].target` resource. This will allow users to specify a template to render any type of manifest, instead of the original Secret object.

Problems with this proposal is that the whole reconciliation logic reads from a secret - this would need to be updated if templates using Manifests are set. In that case, we should query for that specific resource as specified on the target.

```yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
spec:
  target:
    name: target-custom-resource
    manifest: # Information as to find it afterwards during reconcile cycle
      APIVersion: my.custom.api/v1alpha1
      Kind: CustomResource
    template:
      templateFrom:
      - target: Spec #Additional target at root level (instead of data, metadata and annotations level).
      # Other option would be to allow a gjson path such as target:'.spec' where . is the indicator of this type of expression for backwards compatibility.
        literal: |
            customSpecField1: {{ .fromSecretStore }}
            field2:
              couldBeNested:
              - {{ .fromSecretStore }}
          ## Name: is obtained from spec.target.name
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
spec:
  target:
    name: target-configmap
    manifest: # Information as to find it afterwards during reconcile cycle
      APIVersion: v1
      Kind: ConfigMap
    template:
      templateFrom:
      - target: Data
        literal: |
          {{ .field1 }}: {{ .value1 }}
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
spec:
  target:
    name: target-configmap
    manifest: # Information as to find it afterwards during reconcile cycle
      APIVersion: v1
      Kind: ConfigMap # should render with no need of templates - as `.data` is the default Secret target.
```

Only one `templateFrom` entry can be set if its type is `target: Manifest`.

## Consequences

* **First Class support for GitOps tools**: GitOps tools often need configmap information considered as sensitive. This allows better integration with such tools

* **Increased Complexity**: Instead of getting a single secret to start reconciling process, we would need to first verify if `target.manifest` is set - and use the manifest information to get the appropriate resource (defaulting to a kubernetes secret). Templateing Logic would only be increased to some extend, as we would need to change the `Secret` type to a runtime.Object

* **Better Extensibility**: This feature allows ESO to not only be used by operators but also better integrated to systems.

* **Backwards Compatible**: This feature would not change how `target` and `template` are currently used by existing installations.

* **API and Documentation Update**: The API changes need to be well-documented to ensure users understand how to utilize the new feature effectively.


## Acceptance Criteria

* behavior:
  * Reconciliation logic should not change when `target!= Secret`
  * `creationPolicy`, `updatePolicy`, and `deletionPolicy` must be compatible when `target != Secret`
  * One of the two must be implemented:
    * a feature flag `--unsafe-allow-non-secret-targets` must be set to allow this feature. If not set, `template.manifest` should cause error to the reconciliation.
    * Feature is always enabled - but Warnings must be emitted whenever `target.manifest` is used pointing to the use of sensitive information on open manifests.
      * Warnings should be disabled with feature flags
* deployment:
  * Extra RBAC options must be available on helm values (to allow the usage of this feature)
  * Helm values must allow the installation of this new feature (setting up the appropriate feature flags, etc)
* tests: 
  * controller unit tests for `target.manifest` behavior and `target.template.target` behavior
  * controller regression tests for `target.manifest` and `target.template.target` focused on different `creationPolicy` and `deletionPolicy`
  * e2e test for `target.manifest` targeting a ConfigMap (first class support for ArgoCD and Flux)
  * e2e test for `target.manifest` targeting a custom resource

* the API changes need to be documented
    * API/CRD spec inline documentation
    * ExternalSecrets API documentation
    * Guides section for `ExternalSecret` 'Creating Non-Secret Resources'.
    * Warnings on the feature as non-Secret manifests are not meant to contain sensitive information.
