Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions cli/azd/docs/extensions/extension-framework.md
Original file line number Diff line number Diff line change
Expand Up @@ -1323,6 +1323,84 @@ if err := host.Run(ctx); err != nil {

```

## Key Vault Secret Resolution

Extensions frequently need to read secrets that are stored in Azure Key Vault and referenced in the `azd` environment. The `azdext` package ships a `KeyVaultResolver` that handles this transparently for Go-based extensions.

### Automatic Host-Level Resolution

When azd launches an extension, it automatically resolves any Key Vault secret references found in the azd-managed environment variables (`akvs://` and `@Microsoft.KeyVault(...)` formats) before they are passed to the extension process. Plain system environment variables from `os.Environ()` are **not** modified.

This means extension code often does not need to call `KeyVaultResolver` directly — secrets are already resolved by the time the extension starts.

### Using `KeyVaultResolver` in Extension SDK

For cases where an extension needs to resolve Key Vault references itself (for example, when reading references from a config file or constructing references at runtime), the `KeyVaultResolver` type in `pkg/azdext` provides a full-featured client.

**Supported reference formats:**

| Format | Example |
|--------|---------|
| `akvs://` (preferred) | `akvs://<subscription-id>/<vault-name>/<secret-name>` |
| `@Microsoft.KeyVault(SecretUri=...)` | `@Microsoft.KeyVault(SecretUri=https://my-vault.vault.azure.net/secrets/my-secret)` |
| `@Microsoft.KeyVault(VaultName=...;SecretName=...)` | `@Microsoft.KeyVault(VaultName=my-vault;SecretName=my-secret)` |

**Public API:**

- `IsSecretReference(s string) bool` — returns `true` when the string is any of the three supported formats (strips surrounding quotes/whitespace).
- `ParseSecretReference(ref string) (*SecretReference, error)` — parses a reference into vault and secret components.
- `NewKeyVaultResolver(credential azcore.TokenCredential, opts *KeyVaultResolverOptions) (*KeyVaultResolver, error)` — creates a resolver. Use `azdext.NewTokenProvider` to obtain a credential from the extension's access token.
- `Resolve(ctx, ref string) (string, error)` — resolves a single reference to its secret value.
- `ResolveMap(ctx, refs map[string]string) (map[string]string, error)` — resolves a map where all values are secret references.
- `ResolveEnvironment(ctx, env map[string]string) (map[string]string, error)` — resolves a mixed env-var map: values that are secret references are replaced, plain values are passed through unchanged.

**Example:**

```go
import (
"github.com/azure/azure-dev/cli/azd/pkg/azdext"
)

func resolveSecrets(ctx context.Context, azdClient *azdext.AzdClient) error {
// Obtain a credential backed by the extension's azd access token.
tp, err := azdext.NewTokenProvider(ctx, azdClient, nil)
if err != nil {
return fmt.Errorf("creating token provider: %w", err)
}

resolver, err := azdext.NewKeyVaultResolver(tp, nil)
if err != nil {
return fmt.Errorf("creating resolver: %w", err)
}

// Resolve a single reference.
secret, err := resolver.Resolve(ctx, "akvs://my-sub/my-vault/my-secret")
if err != nil {
return fmt.Errorf("resolving secret: %w", err)
}
fmt.Println("secret value:", secret)

// Resolve all Key Vault references in a mixed env map.
env := map[string]string{
"DB_HOST": "mydb.postgres.database.azure.com",
"DB_PASSWORD": "akvs://my-sub/my-vault/db-password",
}
resolved, err := resolver.ResolveEnvironment(ctx, env)
if err != nil {
return fmt.Errorf("resolving env: %w", err)
}
// resolved["DB_HOST"] == "mydb.postgres.database.azure.com" (unchanged)
// resolved["DB_PASSWORD"] == "<actual secret value>"
return nil
}
```

**Error handling:**

All domain errors from `Resolve` (invalid reference, secret not found, authentication failure) are returned as `*KeyVaultResolveError`. You can inspect `err.Reason` (a `ResolveReason` constant) to distinguish between `ResolveReasonInvalidReference`, `ResolveReasonNotFound`, and `ResolveReasonAccessDenied`.

---

## Developer Artifacts

`azd` leverages gRPC for the communication protocol between Core `azd` and extensions. gRPC client & server components are automatically generated from profile files.
Expand Down Expand Up @@ -1735,6 +1813,7 @@ Prompts the user to select a location.

- **Request:** _PromptLocationRequest_
- `azure_context` (AzureContext)
- `allowed_locations` (repeated string): optional list of location names to filter the prompt choices (case-insensitive). When set, only matching locations are shown to the user. An error is returned if the default location is filtered out.
- **Response:** _PromptLocationResponse_
- Contains **Location**

Expand Down
36 changes: 34 additions & 2 deletions cli/azd/docs/external-authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,41 @@ The server should take this request and fetch a token using the given configurat

The message is returned as is as the `error` for the `GetToken` call on the client side.

## Implementation
## Using `azd auth token`

The `azd auth token` command lets external tools and scripts obtain an Azure access token using the currently signed-in azd principal.

### Default output (raw token)

Running the command with no flags prints the raw access token to stdout, making it easy to pipe directly into scripts:

```bash
TOKEN=$(azd auth token)
curl -H "Authorization: Bearer $TOKEN" https://management.azure.com/subscriptions?api-version=2022-09-01
```

### Structured JSON output

Pass `--output json` to receive a structured JSON object that also includes the token expiration time:

```bash
azd auth token --output json
```

```json
{
"token": "<access-token>",
"expiresOn": "2026-03-31T20:00:00Z"
}
```

### Optional flags

- `--scope` — override the requested OAuth scope (can be specified multiple times for multiple scopes). Defaults to the Azure management scope.
- `--tenant-id` — request a token for a specific tenant.
- `--claims` — pass additional claims in the token request.


The `azd` CLI implements the client side of this feature in the [`pkg/auth/remote_credential.go`](../pkg/auth/remote_credential.go).

The VS Code implementation of the server is in [src/utils/authServer.ts](../../../ext/vscode/src/utils/).

Expand Down