Skip to main content
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

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.
// 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

locals "tokens" {
    token = secret("env", "API_TOKEN")
}
Generated Python:
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

locals "secrets" {
    db_pw = secret("aws_secrets_manager", "prod/db/password")
}
Generated Python:
# ── 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

locals "secrets" {
    api_key = secret("vault", "secret/app#key")
}
Generated Python:
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

locals "secrets" {
    api_key = secret("gcp_secret_manager", "projects/my-project/secrets/api-key/versions/latest")
}
Generated Python:
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

locals "secrets" {
    cert = secret("azure_key_vault", "my-cert")
}
Generated Python:
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:
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")
}
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:
// 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 = "..."

ScenarioRecommended approach
Value is in an env var at deploy timesecret("env", "VAR_NAME") or ephemeral input
Value is in AWS Secrets Manager, Vault, GCP, or Azuresecret("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 stateephemeral modifier
Input from secret store AND encrypted in stateephemeral + from = "scheme:path"
See input from for the from = "..." attribute and ephemeral inputs for the ephemeral modifier.