> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tracecat.com/llms.txt
> Use this file to discover all available pages before exploring further.

# External secret stores

> Reference AWS Secrets Manager secrets from Tracecat workspaces: organization-owned stores, IAM AssumeRole trust, and runtime-only resolution.

<Badge icon="lock" color="blue" size="lg" shape="pill">Enterprise Edition</Badge>

## Overview

An external secret store lets a workspace secret point at a value that stays in AWS
Secrets Manager. Tracecat stores only the reference: the store, the secret name or
ARN, and the key mapping. Workflows read `${{ SECRETS.<name>.<key> }}` exactly as
they do for [local secrets](/automations/core-concepts/secrets).

Organization admins own stores under Organization settings, and authorize the
workspaces that may reference each one. Workspace members then create AWS-backed
secrets against an authorized store.

## Trust model

Tracecat assumes a role you own in your AWS account:

* Tracecat calls `sts:AssumeRole` on the role ARN you register, using its own
  workload identity and an external ID that Tracecat generates when you create the
  store. The external ID is server-owned and never changes.
* The assumed session reads the `AWSCURRENT` version of the secret with
  `secretsmanager:GetSecretValue`. Tracecat requests no list, write, delete, or
  rotation permissions.
* Tracecat persists no remote value. Each resolution fetches from AWS, injects the
  value into the action sandbox, and discards it. Nothing is cached between runs.
* Failures are sanitized. Error messages carry a failure code and the AWS error
  code, never any part of the secret value.

## Prerequisites

Your Tracecat deployment must expose the AWS principal that assumes your role. The
operator sets both variables on every Tracecat service:

| Variable                                  | Value                                                                                                       |
| :---------------------------------------- | :---------------------------------------------------------------------------------------------------------- |
| `TRACECAT__AWS_ASSUME_ROLE_ACCOUNT_ID`    | The 12-digit AWS account ID of the Tracecat deployment, such as `210987654321`                              |
| `TRACECAT__AWS_ASSUME_ROLE_PRINCIPAL_ARN` | The full ARN of the Tracecat workload principal, such as `arn:aws:iam::210987654321:role/tracecat-executor` |

On self-hosted deployments, set them in your `.env` and restart the stack; the
Docker Compose files already pass them through.
Until they are set, the store card shows a `<tracecat-principal-arn>` placeholder
instead of a usable trust policy.

The Tracecat workload also needs `sts:AssumeRole` permission on the role ARNs it
will assume.

## Create the IAM role

Create one role per store in the AWS account that holds your secrets.

<Steps>
  <Step title="Create the store in Tracecat first">
    Tracecat generates the external ID when the store is created, so create the store
    in Organization settings before you write the trust policy. Use a placeholder role
    ARN if you have not created the role yet, then update the store with the real ARN.
  </Step>

  <Step title="Attach the trust policy">
    Copy the trust policy from the store card. It names the Tracecat principal and
    conditions the assumption on your store's external ID:

    ```json theme={null}
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "AWS": "arn:aws:iam::210987654321:role/tracecat-executor"
          },
          "Action": "sts:AssumeRole",
          "Condition": {
            "StringEquals": {
              "sts:ExternalId": "tracecat-EXAMPLE-external-id"
            }
          }
        }
      ]
    }
    ```

    The `sts:ExternalId` condition is what prevents a confused deputy. Keep it.
  </Step>

  <Step title="Attach the permissions policy">
    Scope `secretsmanager:GetSecretValue` to the exact secrets you intend to share.
    The store card shows a wildcard starting point; narrow the `Resource` list before
    you use it in production:

    ```json theme={null}
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": ["secretsmanager:GetSecretValue"],
          "Resource": [
            "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/app/api-key-AbCdEf",
            "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/app/db-*"
          ]
        },
        {
          "Effect": "Allow",
          "Action": ["kms:Decrypt"],
          "Resource": "*",
          "Condition": {
            "StringEquals": {
              "kms:ViaService": "secretsmanager.us-east-1.amazonaws.com"
            }
          }
        }
      ]
    }
    ```

    The `kms:Decrypt` statement is required only for secrets encrypted with a
    customer-managed KMS key. Scope its `Resource` to that key ARN when you use one.
  </Step>
</Steps>

## Add a store

Go to Organization settings → Secret stores and select `Add store`. You need
the `org:secret:create` scope.

| Field          | Value                                                                                  |
| :------------- | :------------------------------------------------------------------------------------- |
| Name           | Unique within the organization, such as `production-secrets`                           |
| Role ARN       | The role you created, such as `arn:aws:iam::123456789012:role/tracecat-secrets-reader` |
| Region         | The Secrets Manager region, such as `us-east-1`                                        |
| Enabled        | Turn off to block resolution for every reference at once                               |
| All workspaces | Allow every current and future workspace in the organization                           |

<img src="https://mintcdn.com/tracecat/CihzPTTCd3OS7XKO/img/automations/secret-stores/create-store.png?fit=max&auto=format&n=CihzPTTCd3OS7XKO&q=85&s=c1bb1904b8f747293ccf89f0695c5d86" alt="Create an AWS Secrets Manager store" width="898" height="1112" data-path="img/automations/secret-stores/create-store.png" />

After you save, the connection details expand. Copy the generated external ID,
trust policy, and permissions policy into AWS. To show them again, expand
`Connection details` on the store card.
A store reads secrets in one region only. Create a second store for a second region.

<img src="https://mintcdn.com/tracecat/CihzPTTCd3OS7XKO/img/automations/secret-stores/store-policies.png?fit=max&auto=format&n=CihzPTTCd3OS7XKO&q=85&s=750b4c0db022c2fbe37b1d8a28afbeb5" alt="Store card with external ID, trust policy, and permissions policy" width="898" height="1112" data-path="img/automations/secret-stores/store-policies.png" />

To change a store's name, role ARN, or region, open its actions menu and choose
`Edit store`. The external ID stays the same for the life of the store.

<img src="https://mintcdn.com/tracecat/CihzPTTCd3OS7XKO/img/automations/secret-stores/edit-store.png?fit=max&auto=format&n=CihzPTTCd3OS7XKO&q=85&s=1d58ee8f11fa7098d2e6e44ef473576e" alt="Edit the store connection" width="898" height="1112" data-path="img/automations/secret-stores/edit-store.png" />

## Authorize workspaces

Open the `Workspaces` picker on the store card. Choose `All workspaces`
to allow every current and future workspace in your organization. Choose
`Selected workspaces` to manage access using individual workspace checkboxes.

Switching to `Selected workspaces` preserves individual authorizations and access for
workspaces that already reference the store. Remove those references before
unchecking a workspace. To stop secret resolution everywhere, turn off
`Enabled` on the store.

Workspace users see the store's name, description, provider, region, and enabled
flag, never its external ID or role ARN.

<img src="https://mintcdn.com/tracecat/CihzPTTCd3OS7XKO/img/automations/secret-stores/authorize-workspace.png?fit=max&auto=format&n=CihzPTTCd3OS7XKO&q=85&s=fc802f9f379ad6e2522173bb60afc6d2" alt="Authorize a workspace on the store" width="898" height="1112" data-path="img/automations/secret-stores/authorize-workspace.png" />

## Create an AWS-backed secret

In the workspace, open `/credentials`, select `Add credential`, and switch the
source from `Tracecat` to `AWS Secrets Manager`.

| Field              | Value                                                                                      |
| :----------------- | :----------------------------------------------------------------------------------------- |
| Name               | Snake case. Referenced as `SECRETS.<name>.<key>`                                           |
| Environment        | Defaults to `default`                                                                      |
| AWS secret store   | Only enabled stores authorized for this workspace are listed                               |
| Secret name or ARN | The friendly name as shown in the AWS console, such as `prod/app/api-key`, or the full ARN |
| Value mapping      | How the remote `SecretString` becomes secret keys                                          |

A friendly name is looked up in the store's region. A full ARN must be in the
store's region; Tracecat rejects the reference at save time otherwise.

AWS-backed secrets are always the `custom` type, and they never accept key values.
To change one, edit its reference or key mapping.

<img src="https://mintcdn.com/tracecat/CihzPTTCd3OS7XKO/img/automations/secret-stores/aws-secret-reference.png?fit=max&auto=format&n=CihzPTTCd3OS7XKO&q=85&s=bd4665fd0fc252413812319f6cf878f6" alt="Create an AWS-backed secret with a JSON field mapping" width="898" height="1112" data-path="img/automations/secret-stores/aws-secret-reference.png" />

### Value mapping

`Whole string → one key` binds the entire `SecretString` to exactly one key:

```yaml theme={null}
# SecretString: sk-live-abc123
# Output key:   API_TOKEN
${{ SECRETS.stripe.API_TOKEN }}
```

`JSON object → selected fields` parses the `SecretString` as a JSON object and
maps selected top-level fields onto keys you name. The field must exist and its
value must be a string:

```yaml theme={null}
# SecretString: {"username": "svc_app", "password": "hunter2", "port": 5432}
# Mapping: USERNAME ← username, PASSWORD ← password
${{ SECRETS.app_db.USERNAME }}
${{ SECRETS.app_db.PASSWORD }}
```

Selecting `port` would fail with `non_string_field` because its value is a number.
Output keys must be unique, and a mapping declares at most 100 keys or fields.

## Verify a reference

Use `Edit` on an AWS-backed credential to change its store, secret name or ARN,
and key mapping without deleting the credential.

<img src="https://mintcdn.com/tracecat/CihzPTTCd3OS7XKO/img/automations/secret-stores/edit-reference.png?fit=max&auto=format&n=CihzPTTCd3OS7XKO&q=85&s=3f5509c79895136da81a02e464708ecf" alt="Edit an AWS secret reference and key mapping" width="898" height="1112" data-path="img/automations/secret-stores/edit-reference.png" />

The `Check` button resolves the reference server-side and reports `Reachable` with the
resolved key names on hover, or the failure code. The check never returns the
remote value to the browser.

Run a check after you create a reference, after you change an IAM policy, and
after you move a secret between accounts or regions.

<img src="https://mintcdn.com/tracecat/CihzPTTCd3OS7XKO/img/automations/secret-stores/check-reachable.png?fit=max&auto=format&n=CihzPTTCd3OS7XKO&q=85&s=25f9b61e51218598941e17ed63e700c5" alt="AWS-backed credentials showing Reachable checks" width="898" height="1112" data-path="img/automations/secret-stores/check-reachable.png" />

## Runtime behavior

When an action requests an AWS-backed secret, Tracecat loads the reference rows,
closes the database session, then resolves every reference in parallel. Reads are
deduplicated per store and remote reference within one resolution, so a secret
mapped onto two aliases costs one `GetSecretValue` call.

A configured AWS-backed secret fails loudly even when the action declares it
optional: a missing store grant or an unreadable remote secret raises rather than
silently skipping the credential. The error names the secret and environment, and
carries the resolution error code.

Connection attempts time out after 5 seconds, reads after 10 seconds, and the AWS
SDK retries up to 3 times.

## Lifecycle rules

* You cannot delete a store while any workspace secret references it. Delete the
  references first; the store card shows the reference count and disables the
  delete button.
* You cannot revoke a workspace authorization while that workspace still holds
  secrets referencing the store. Delete those secrets first.
* Disabling a store fails every resolution with `store_disabled` while keeping the
  references intact. Use it to cut access without deleting configuration.
* Deleting an organization deletes its stores and authorizations.
* Deleting a workspace deletes its store authorizations.

## Troubleshooting

Resolution failures surface one of these codes in the check result and in the
action error:

| Code                 | Cause                                            | Fix                                                                                                                |
| :------------------- | :----------------------------------------------- | :----------------------------------------------------------------------------------------------------------------- |
| `store_disabled`     | The store's enabled toggle is off                | Re-enable the store in Organization settings                                                                       |
| `assume_role_failed` | `sts:AssumeRole` was denied                      | Check the trust policy names the Tracecat principal and the store's external ID                                    |
| `access_denied`      | The assumed role cannot read that secret         | Add the secret ARN to the role's `secretsmanager:GetSecretValue` resources, and check the secret's resource policy |
| `not_found`          | No such secret in the store's region             | Check the name or ARN, and that it exists in the store's region                                                    |
| `decryption_failed`  | The role cannot use the secret's KMS key         | Grant `kms:Decrypt` on that key to the role, and allow the role in the key policy                                  |
| `throttled`          | Secrets Manager or STS rate limits               | Reduce concurrency, or request a quota increase                                                                    |
| `timeout`            | The AWS call did not complete in time            | Check network egress from the Tracecat deployment to AWS                                                           |
| `binary_value`       | The secret holds `SecretBinary`                  | Store the value as `SecretString`                                                                                  |
| `malformed_json`     | JSON mapping, but the value is not a JSON object | Switch to whole-string mapping, or store a JSON object                                                             |
| `missing_field`      | A mapped field is absent from the JSON object    | Correct the field name, or add the field in AWS                                                                    |
| `non_string_field`   | A mapped field is not a string                   | Map a string field, or store the value as a string                                                                 |
| `invalid_mapping`    | AWS rejected the request parameters              | Check the secret name or ARN format                                                                                |
| `region_mismatch`    | The ARN's region differs from the store's region | Use an ARN in the store's region, or create a store in the ARN's region                                            |
| `unknown`            | An unclassified AWS error                        | Check the reported AWS error code                                                                                  |

## Limits

* AWS Secrets Manager is the only supported provider.
* One store reads one region.
* Tracecat reads the `AWSCURRENT` version stage only. Staged and previous versions
  are not addressable.
* A key mapping declares at most 100 output keys.
* Rotating a secret in AWS needs no change in Tracecat: the next resolution reads
  the new `AWSCURRENT` value. Agent LLM requests can keep using the previous value
  for up to 60 seconds.
* The LiteLLM service resolves AWS-backed agent LLM credentials with its own AWS
  principal. When that principal differs from the one in your trust policy, as on
  the Fargate deployment, those requests fail with `assume_role_failed`. Store
  agent LLM credentials as local secrets on those deployments.

## Related pages

* See [Secrets](/automations/core-concepts/secrets) for local secrets, secret types,
  environments, and expression syntax.
* See [Environment variables](/self-hosting/environment-variables) for where the
  operator sets the AWS AssumeRole variables on a self-hosted deployment.
* See [RBAC](/manage-platform/rbac) for the `org:secret:*` scopes that gate store
  management.
