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

# secret() built-in

> Fetch sensitive values from external secret stores at apply time — env, AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager, or Azure Key Vault.

`secret()` is an expression-level built-in that reads a sensitive value from an external secret store when Pulumi runs `pulumi up`. The value is never written to source code or to the Pulumi state file in plain text.

## Syntax

```xcl theme={"theme":"css-variables"}
secret("backend", "path")
```

Both arguments must be string literals. Non-literal arguments (variables, expressions) are a parse error — the backend and path must be known at compile time so ubx can emit the correct helper function.

```xcl theme={"theme":"css-variables"}
// valid
pw = secret("aws_secrets_manager", "prod/db/password")
tok = secret("env", "API_TOKEN")

// parse error — arguments must be string literals
pw = secret(backend_name, secret_path)
```

## Backends

ubx supports five backends. The backend name is the first argument to `secret()`.

### `env` — environment variable

```xcl theme={"theme":"css-variables"}
locals "tokens" {
    token = secret("env", "API_TOKEN")
}
```

Generated Python:

```python theme={"theme":"css-variables"}
token = os.environ.get("API_TOKEN", "")
```

The `env` backend is **Resolved\<T>** — the value is read inline via `os.environ.get()` and does not involve any async Pulumi Output. Use it for values that are present in the deployment environment but should not appear in source code.

### `aws_secrets_manager` — AWS Secrets Manager

```xcl theme={"theme":"css-variables"}
locals "secrets" {
    db_pw = secret("aws_secrets_manager", "prod/db/password")
}
```

Generated Python:

```python theme={"theme":"css-variables"}
# ── Secret helpers ────────────────────────────────────────────────────────────
def __ubx_aws_secret(name: str) -> str:
    import boto3
    return boto3.client("secretsmanager").get_secret_value(SecretId=name)["SecretString"]

# ── Locals ────────────────────────────────────────────────────────────────────
db_pw = pulumi.Output.secret(__ubx_aws_secret("prod/db/password"))
```

The value is fetched at apply time and immediately wrapped in `pulumi.Output.secret()`, which instructs Pulumi to encrypt it in the state file. The boto3 client uses ambient AWS credentials (environment variables, EC2 instance profile, or AWS SSO).

### `vault` — HashiCorp Vault

```xcl theme={"theme":"css-variables"}
locals "secrets" {
    api_key = secret("vault", "secret/app#key")
}
```

Generated Python:

```python theme={"theme":"css-variables"}
def __ubx_vault_secret(path: str) -> str:
    import hvac, os as _os
    _c = hvac.Client(url=_os.environ.get("VAULT_ADDR", "http://127.0.0.1:8200"), token=_os.environ.get("VAULT_TOKEN"))
    _parts = path.split("#", 1)
    _data = _c.secrets.kv.read_secret_version(path=_parts[0])["data"]["data"]
    return _data[_parts[1]] if len(_parts) > 1 else next(iter(_data.values()))

api_key = pulumi.Output.secret(__ubx_vault_secret("secret/app#key"))
```

**Vault path format:** `mount/path#field` — the `#` separator selects a specific field from the secret's key-value map. When `#field` is omitted, the first value in the map is returned. The helper reads from the KV v2 engine and uses `VAULT_ADDR` and `VAULT_TOKEN` from the environment.

### `gcp_secret_manager` — GCP Secret Manager

```xcl theme={"theme":"css-variables"}
locals "secrets" {
    api_key = secret("gcp_secret_manager", "projects/my-project/secrets/api-key/versions/latest")
}
```

Generated Python:

```python theme={"theme":"css-variables"}
def __ubx_gcp_secret(name: str) -> str:
    from google.cloud import secretmanager as _sm
    return _sm.SecretManagerServiceClient().access_secret_version(request={"name": name}).payload.data.decode()

api_key = pulumi.Output.secret(__ubx_gcp_secret("projects/my-project/secrets/api-key/versions/latest"))
```

Pass the full resource name including project, secret, and version. Application Default Credentials are used for authentication.

### `azure_key_vault` — Azure Key Vault

```xcl theme={"theme":"css-variables"}
locals "secrets" {
    cert = secret("azure_key_vault", "my-cert")
}
```

Generated Python:

```python theme={"theme":"css-variables"}
def __ubx_azure_secret(name: str) -> str:
    from azure.keyvault.secrets import SecretClient as _SC
    from azure.identity import DefaultAzureCredential as _DAC
    import os as _os
    return _SC(vault_url=_os.environ.get("AZURE_VAULT_URL", ""), credential=_DAC()).get_secret(name).value

cert = pulumi.Output.secret(__ubx_azure_secret("my-cert"))
```

Set `AZURE_VAULT_URL` to your vault's endpoint (e.g., `https://myvault.vault.azure.net`). `DefaultAzureCredential` tries environment variables, managed identity, and Azure CLI in order.

## Pending\<T> propagation

`env` is **Resolved\<T>** — the value is a plain string at runtime. All other backends are **Pending\<T>** — the value is a `pulumi.Output[str]`, because it is fetched asynchronously and wrapped in `pulumi.Output.secret()`.

Pending values propagate through the expression tree. Passing a non-`env` secret directly to a resource property is always valid — Pulumi accepts `Input[T]` (which includes `Output[T]`) for all resource properties. But including a Pending secret in a sync context (such as `values {}` inside a `sync argocd` block) will trigger `.apply()` wrapping automatically.

## Helper deduplication

Helper functions are emitted once per file, before the stack code. If multiple `secret()` calls use the same backend, the helper is only emitted once regardless of how many times it is called:

```xcl theme={"theme":"css-variables"}
locals "secrets" {
    pw1 = secret("aws_secrets_manager", "prod/db/password")
    pw2 = secret("aws_secrets_manager", "prod/db/replica-password")
    tok = secret("aws_secrets_manager", "prod/auth/token")
}
```

```python theme={"theme":"css-variables"}
def __ubx_aws_secret(name: str) -> str:  # emitted once
    import boto3
    return boto3.client("secretsmanager").get_secret_value(SecretId=name)["SecretString"]

pw1 = pulumi.Output.secret(__ubx_aws_secret("prod/db/password"))
pw2 = pulumi.Output.secret(__ubx_aws_secret("prod/db/replica-password"))
tok = pulumi.Output.secret(__ubx_aws_secret("prod/auth/token"))
```

## Unknown backend

An unrecognized backend name produces a **warning** at compile time and is treated as Pending\<T>. The generated code will call a helper function matching the pattern `__ubx_<backend>_secret(path)`, which you must define yourself:

```xcl theme={"theme":"css-variables"}
// warning: unknown secret backend "hashicorp_cloud" — treating as Pending<T>
pw = secret("hashicorp_cloud", "my-org/my-app/db-password")
```

## When to use `secret()` vs `ephemeral` vs `from = "..."`

| Scenario                                                            | Recommended approach                             |
| ------------------------------------------------------------------- | ------------------------------------------------ |
| Value is in an env var at deploy time                               | `secret("env", "VAR_NAME")` or `ephemeral` input |
| Value is in AWS Secrets Manager, Vault, GCP, or Azure               | `secret("backend", "path")` in a `locals` block  |
| Input should always come from a secret store (no manual config set) | `from = "scheme:path"` on an input field         |
| Input should be encrypted in Pulumi state                           | `ephemeral` modifier                             |
| Input from secret store AND encrypted in state                      | `ephemeral` + `from = "scheme:path"`             |

See [input from](/v1/xcl/language/input-from) for the `from = "..."` attribute and [ephemeral inputs](/v1/xcl/language/ephemeral) for the `ephemeral` modifier.
