For detailed information about configuring Kubernetes ESO with Secret Server and the Delinea Platform, see the Delinea External Secrets Operator integration documentation: https://docs.delinea.com/online-help/integrations/external-secrets/kubernetes-eso-secret-server.htm
You need a username, password and a fully qualified Secret-Server/Platform tenant URL to authenticate
i.e. https://yourTenantName.secretservercloud.com or https://yourtenantname.delinea.app.
Both username and password can be specified either directly in your SecretStore yaml config, or by referencing a Kubernetes Secret.
For production deployments, prefer Kubernetes Secret references over direct values.
Note: Use https://yourtenantname.secretservercloud.com for Secret Server or https://yourtenantname.delinea.app for Platform.
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: secret-server-store-values
spec:
provider:
secretserver:
serverURL: "https://yourtenantname.secretservercloud.com" # or "https://yourtenantname.delinea.app" for Platform
username:
value: "yourusername"
password:
value: "yourpassword"
apiVersion: v1
kind: Secret
metadata:
name: secretserver-credentials
type: Opaque
stringData:
username: "yourusername"
password: "yourpassword"
---
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: secret-server-store
spec:
provider:
secretserver:
serverURL: "https://yourtenantname.secretservercloud.com"
username:
secretRef:
name: secretserver-credentials
key: username
password:
secretRef:
name: secretserver-credentials
key: password
If your Secret Server account requires a domain, set domain with the same value used for interactive or API login.
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: secret-server-store-domain
spec:
provider:
secretserver:
serverURL: "https://yourtenantname.secretservercloud.com"
domain: "my-domain"
username:
secretRef:
name: secretserver-credentials
key: username
password:
secretRef:
name: secretserver-credentials
key: password
For Secret Server instances that use a private CA, configure either caBundle or caProvider.
This example reads the CA certificate from a ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: secretserver-ca
data:
ca.crt: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
---
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: secret-server-store-ca
spec:
provider:
secretserver:
serverURL: "https://secretserver.example.com"
caProvider:
type: ConfigMap
name: secretserver-ca
key: ca.crt
username:
secretRef:
name: secretserver-credentials
key: username
password:
secretRef:
name: secretserver-credentials
key: password
As an alternative to username and password, you can authenticate using a pre-issued
access token via the token field. This is useful when username/password login is not
available (for example, on-prem installations with domain accounts). When token is set,
username and password are not required and are ignored.
Like the other credentials, the token can be provided directly via the value field or by
referencing a Kubernetes secret via secretRef.
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: secret-server-store
spec:
provider:
secretserver:
serverURL: "https://yourtenantname.secretservercloud.com" # or "https://yourtenantname.delinea.app" for Platform
token:
secretRef:
name: <NAME_OF_K8S_SECRET>
key: <KEY_IN_K8S_SECRET>
Secrets can be referenced using four different key formats in the remoteRef.key field:
| Format | Example | Description |
|---|---|---|
| Secret ID | 52622 |
Numeric ID of the secret. Always unambiguous. |
| Secret Name | my-secret |
Name of the secret. If multiple secrets share the same name across different folders, the first match is returned. |
| Secret Path | /FolderName/SecretName |
Full folder path including the secret name. Uniquely identifies a secret across folders. |
| Folder-scoped Name | folderId:73/my-secret |
Name-based lookup scoped to a specific folder ID. Use this when multiple secrets share the same name in different folders and you need to target a specific one. |
Notes:
folderId:<id>/<name>) is particularly important when using PushSecret with deletionPolicy: Delete, because the deletion and existence-check operations need to identify the correct secret without access to metadata. See Pushing Secrets for details.Secret Server returns a secret object with an Items array. Individual ItemValue fields can contain JSON or plain text.
Set remoteRef.property to a gjson path when the first ItemValue contains JSON, or to a field Slug/FieldName for multi-field templates.
If remoteRef.property is empty, data returns the full Secret Server secret object as JSON.
For dataFrom with an empty property, the provider parses the first field's ItemValue as a map when it is a valid JSON object; otherwise it maps the secret into key/value entries keyed by each field's Slug (falling back to FieldName, then FieldID).
For dataFrom with property set, the provider resolves that property using the same rules as data; if the resolved value is itself a JSON object it is expanded into key/value entries, otherwise a single entry keyed by the property name is returned.
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: secret-server-external-secret
spec:
refreshInterval: 1h0m0s
secretStoreRef:
kind: SecretStore
name: secret-server-store
data:
- secretKey: SecretServerValue #<SECRET_VALUE_RETURNED_HERE>
remoteRef:
key: "52622" #<SECRET_ID>
property: "array.0.value" #<GJSON_PROPERTY> * an empty property will return the entire secret
While Secret-Server/Platform always returns secrets in JSON format with an Items array structure, individual field values (stored in ItemValue) may contain plain text, passwords, URLs, or other non-JSON content.
When retrieving fields that contain plain text values, you can reference them directly by their FieldName or Slug without needing additional JSON parsing within the ItemValue.
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: secret-server-external-secret
spec:
refreshInterval: 1h0m0s
secretStoreRef:
kind: SecretStore
name: secret-server-store
data:
- secretKey: password
remoteRef:
key: "52622" # Secret ID
property: "password" # FieldName or Slug of the password field
In this example, if the secret contains an Item with FieldName: "Password" or Slug: "password", the plain text value stored in ItemValue is retrieved directly and stored under the key password in the Kubernetes Secret.
This approach works for any field type (text, password, URL, etc.) where the ItemValue contains simple content rather than nested JSON structures.
In addition to retrieving secrets by ID or Name, the Secret-Server/Platform provider now supports fetching secrets by path. This allows you to specify a secret’s folder hierarchy and name in the format:
/FolderName/SecretName
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: secret-server-external-secret
spec:
refreshInterval: 15s
secretStoreRef:
kind: SecretStore
name: secret-server-store
data:
- secretKey: SecretServerValue # Key in the Kubernetes Secret
remoteRef:
key: "/secretFolder/secretname" # Path format: /<Folder>/<SecretName>
# property omitted: returns the entire secret as JSON. Set it to a gjson path or a field Slug/FieldName to select a value.
The path must exactly match the folder and secret name in Secret-Server/Platform. If multiple secrets with the same name exist in different folders, the path helps to uniquely identify the correct one. You can still use property to match fields by Slug/FieldName, extract values from JSON-formatted secrets via gjson, or omit it to retrieve the entire secret.
You can either retrieve your entire secret, match a field by its Slug or FieldName, or use a JSON formatted string
stored in your secret located at Items[0].ItemValue to retrieve a specific value using gjson syntax.
See example JSON secret below.
Using the json formatted secret below:
spec.data.remoteRef.key = 52622 (id of the secret)
spec.data.remoteRef.property = "user" (Items.0.ItemValue user attribute)
returns: marktwain@hannibal.com
spec.data.remoteRef.key = "external-secret-testing" (name of the secret)
spec.data.remoteRef.property = "books.1" (Items.0.ItemValue books.1 attribute)
returns: huckleberryFinn
spec.data.remoteRef.key = "52622" (id of the secret)
spec.data.remoteRef.property = ""
returns: The entire secret in JSON format as displayed below
{
"Name": "external-secret-testing",
"FolderID": 73,
"ID": 52622,
"SiteID": 1,
"SecretTemplateID": 6098,
"SecretPolicyID": -1,
"PasswordTypeWebScriptID": -1,
"LauncherConnectAsSecretID": -1,
"CheckOutIntervalMinutes": -1,
"Active": true,
"CheckedOut": false,
"CheckOutEnabled": false,
"AutoChangeEnabled": false,
"CheckOutChangePasswordEnabled": false,
"DelayIndexing": false,
"EnableInheritPermissions": true,
"EnableInheritSecretPolicy": true,
"ProxyEnabled": false,
"RequiresComment": false,
"SessionRecordingEnabled": false,
"WebLauncherRequiresIncognitoMode": false,
"Items": [
{
"ItemID": 280265,
"FieldID": 439,
"FileAttachmentID": 0,
"FieldName": "Data",
"Slug": "data",
"FieldDescription": "json text field",
"Filename": "",
"ItemValue": "{ \"user\": \"marktwain@hannibal.com\", \"occupation\": \"author\",\"books\":[ \"tomSawyer\",\"huckleberryFinn\",\"Pudd'nhead Wilson\"] }",
"IsFile": false,
"IsNotes": false,
"IsPassword": false
}
]
}
When property is set, the provider first treats the first field's ItemValue as JSON — when it is valid JSON — and extracts the property using gjson syntax (supporting nested paths like "books.1"). If that does not yield a value, it falls back to matching property against each field's Slug or FieldName and returns the corresponding ItemValue. This works for secrets with any number of fields.
Using the json formatted secret below:
spec.data.remoteRef.key = 4000 (id of the secret)
spec.data.remoteRef.property = "Username" (Items.0.FieldName)
returns: usernamevalue
spec.data.remoteRef.key = "Secretname" (name of the secret)
spec.data.remoteRef.property = "password" (Items.1.slug)
returns: passwordvalue
spec.data.remoteRef.key = "4000" (id of the secret)
returns: The entire secret in JSON format as displayed below
{
"Name": "Secretname",
"FolderID": 0,
"ID": 4000,
"SiteID": 0,
"SecretTemplateID": 0,
"LauncherConnectAsSecretID": 0,
"CheckOutIntervalMinutes": 0,
"Active": false,
"CheckedOut": false,
"CheckOutEnabled": false,
"AutoChangeEnabled": false,
"CheckOutChangePasswordEnabled": false,
"DelayIndexing": false,
"EnableInheritPermissions": false,
"EnableInheritSecretPolicy": false,
"ProxyEnabled": false,
"RequiresComment": false,
"SessionRecordingEnabled": false,
"WebLauncherRequiresIncognitoMode": false,
"Items": [
{
"ItemID": 0,
"FieldID": 0,
"FileAttachmentID": 0,
"FieldName": "Username",
"Slug": "username",
"FieldDescription": "",
"Filename": "",
"ItemValue": "usernamevalue",
"IsFile": false,
"IsNotes": false,
"IsPassword": false
},
{
"ItemID": 0,
"FieldID": 0,
"FileAttachmentID": 0,
"FieldName": "Password",
"Slug": "password",
"FieldDescription": "",
"Filename": "",
"ItemValue": "passwordvalue",
"IsFile": false,
"IsNotes": false,
"IsPassword": false
}
]
}
GetAllSecrets is not implemented. The current tss-sdk-go search API used by this provider is capped at 30 results and does not expose pagination, folder enumeration, or server-side tag filtering.dataFrom.find.tags is not supported. Secret Server does not expose an indexed tag search for ESO to map directly to this feature.remoteRef.version returns an error.folderId:<id>/<name> for production references.SecretStore or ClusterSecretStore; avoid committing direct credential values to manifests.SecretStore resources when different teams or namespaces should have different Secret Server access boundaries.ClusterSecretStore only when the same Secret Server account is intentionally shared across namespaces. For ClusterSecretStore, credential secretRef values must include explicit namespaces.PushSecret, grant create/update/delete only in folders that ESO is expected to manage.folderId:<id>/<name>, full paths, or numeric IDs for PushSecret resources with deletionPolicy: Delete to avoid acting on the wrong secret.caBundle or caProvider instead of disabling TLS verification outside ESO.The Delinea Secret-Server/Platform provider supports pushing secrets from Kubernetes back to your Secret Server instance using the PushSecret resource. You can both create new secrets and update existing ones.
When using PushSecret, the remoteRef.remoteKey field determines how the provider identifies
the target secret in Secret Server. The same key formats described in Referencing Secrets apply here:
| Format | Example | When to Use |
|---|---|---|
| Secret ID | 52622 |
Updating an existing secret by its numeric ID. |
| Secret Name | my-secret |
Simple environments where secret names are unique across all folders. |
| Secret Path | /FolderName/SecretName |
When you know the full folder path of the secret. |
| Folder-scoped Name | folderId:73/my-secret |
Recommended for new secrets. Ensures all operations (push, delete, existence check) target the correct folder. |
Why the folder-scoped name format matters:
The PushSecret controller performs three distinct operations on secrets: push (create/update),
delete, and existence check. While the push operation has access to the metadata field
(which can carry a folderId), the delete and existence-check operations only receive the
remoteKey and property — they do not have access to metadata.
This means that if you use a plain secret name like my-secret and multiple secrets with that name
exist in different folders, the delete and existence-check operations cannot distinguish between them
and will act on the first match returned by the API.
By using the folderId:<id>/<name> format (e.g., folderId:73/my-secret), the folder ID is
encoded directly in the key and is available to all operations, ensuring consistent behavior.
Precedence rule: If both a folderId in the remoteKey and a folderId in the metadata are
specified, the value from the remoteKey takes precedence for lookups. The metadata folderId and
secretTemplateId are still required when creating a new secret (they tell the API which folder
and template to use for the new secret).
When creating a new secret in Secret Server, you must provide a folderId and a secretTemplateId. These are passed as metadata in the PushSecret spec:
apiVersion: external-secrets.io/v1alpha1
kind: PushSecret
metadata:
name: push-secret-example
spec:
refreshInterval: 1h
secretStoreRefs:
- name: secret-server-store
kind: SecretStore
selector:
secret:
name: my-k8s-secret
data:
- match:
secretKey: username
remoteRef:
remoteKey: "folderId:73/my-new-secret" # Folder-scoped name ensures correct matching for all operations
property: username # Maps to the 'Username' field/slug in Secret Server
metadata:
apiVersion: kubernetes.external-secrets.io/v1alpha1
kind: PushSecretMetadata
spec:
folderId: 73 # Required for new secrets: folder to create the secret in
secretTemplateId: 6098 # Required for new secrets: template to use
- match:
secretKey: password
remoteRef:
remoteKey: "folderId:73/my-new-secret"
property: password # Maps to the 'Password' field/slug in Secret Server
metadata:
apiVersion: kubernetes.external-secrets.io/v1alpha1
kind: PushSecretMetadata
spec:
folderId: 73
secretTemplateId: 6098
Note: The
folderIdin theremoteKey(folderId:73/...) is used when looking up the secret (for push, delete, and existence checks). ThefolderIdandsecretTemplateIdinmetadataare used when creating a new secret via the Secret Server API.
When updating an existing secret, you do not strictly need the folderId or secretTemplateId metadata, as the provider will fetch the existing secret by its name or ID to update the corresponding fields.
However, if multiple secrets share the same name across different folders, you should use either the
folderId:<id>/<name> format, a path-based key, or a numeric ID to ensure the correct secret is
updated. Using a plain name will update the first match returned by the API.
The PushSecret resource allows you to configure what happens to the remote secret in Secret Server when the PushSecret itself is deleted, via the PushSecret.spec.deletionPolicy field. Supported values are:
Retain: (Default) The remote secret is left intact in Secret Server when the PushSecret is deleted.Delete: The provider will attempt to delete the remote secret from Secret Server when the PushSecret is removed.When Delete is specified, the deletion operation is idempotent; if the secret has already been removed or cannot be found, the provider will safely ignore the error and proceed.
Important: The deletion operation does not have access to metadata. If your Secret Server
has multiple secrets with the same name in different folders and you use deletionPolicy: Delete,
you must use a key format that uniquely identifies the secret — either folderId:<id>/<name>,
a full path (/Folder/SecretName), or a numeric ID. Using a plain name risks deleting the wrong
secret.
If you omit property from the remoteRef, the provider writes the value selected by data.match.secretKey (e.g., the content stored under the config key in your Kubernetes Secret) into the first field of the Secret Server secret. This is useful when your secret value is a single JSON payload that you want to store in a text field like Data or Notes.
apiVersion: external-secrets.io/v1alpha1
kind: PushSecret
metadata:
name: push-secret-json-example
spec:
refreshInterval: 1h
secretStoreRefs:
- name: secret-server-store
kind: SecretStore
selector:
secret:
name: my-k8s-json-secret
data:
- match:
secretKey: config # The key in your k8s secret whose value will be pushed
remoteRef:
remoteKey: "folderId:73/my-new-json-secret"
# property is omitted: the value is stored in the first template field
metadata:
apiVersion: kubernetes.external-secrets.io/v1alpha1
kind: PushSecretMetadata
spec:
folderId: 73
secretTemplateId: 6098