# SkillPatch skill: azure-keyvault-secrets-ts

This skill provides TypeScript/JavaScript workflows for managing secrets and keys using the Azure Key Vault SDK (@azure/keyvault-secrets and @azure/keyvault-keys). It covers authentication setup, CRUD operations for secrets (create, get, list, delete, purge, recover), and key management operations (create RSA/EC keys, get, list, rotate, delete). Designed to help agents interact with Azure Key Vault programmatically in Node.js/TypeScript environments.

You (the agent) were given this URL and asked to use this skill. This is a **public** skill — no SkillPatch account, API key, or prior setup is required. Two ways to use it:

**1. Use it right now** — the skill's complete file tree (SKILL.md + all reference files) is inlined below; read `SKILL.md` first, then follow it, consulting the other files as it directs.

**2. Install the exact package onto disk** (recommended if you can run a shell — this reproduces the skill byte-for-byte, including any binary assets that can't be inlined):

```bash
mkdir -p .claude/skills/azure-keyvault-secrets-ts
curl -sSL https://skillpatch.dev/install_skill/azure-keyvault-secrets-ts | tar -xz -C .claude/skills/
```

(`.claude/skills/` is Claude Code's convention; use whatever directory your agent loads skills from.)


---

## Skill files (3)

- `SKILL.md`
- `references/keys.md`
- `references/secrets.md`


### `SKILL.md`

````markdown
---
name: azure-keyvault-secrets-ts
description: Manage secrets using Azure Key Vault Secrets SDK for JavaScript (@azure/keyvault-secrets). Use when storing and retrieving application secrets or configuration values.
license: MIT
metadata:
  author: Microsoft
  version: "1.0.0"
  package: '@azure/keyvault-secrets'
---

# Azure Key Vault Secrets SDK for TypeScript

Manage secrets with Azure Key Vault.

## Installation

```bash
# Secrets SDK
npm install @azure/keyvault-secrets @azure/identity
```

## Environment Variables

```bash
KEY_VAULT_URL=https://<vault-name>.vault.azure.net
# Or
AZURE_KEYVAULT_NAME=<vault-name>
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production
```

## Authentication

```typescript
import { DefaultAzureCredential, ManagedIdentityCredential } from "@azure/identity";
import { SecretClient } from "@azure/keyvault-secrets";

// Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>
const credential = new DefaultAzureCredential({requiredEnvVars: ["AZURE_TOKEN_CREDENTIALS"]});
// Or use a specific credential directly in production:
// See https://learn.microsoft.com/javascript/api/overview/azure/identity-readme?view=azure-node-latest#credential-classes
// const credential = new ManagedIdentityCredential();
const vaultUrl = `https://${process.env.AZURE_KEYVAULT_NAME}.vault.azure.net`;

const keyClient = new KeyClient(vaultUrl, credential);
const secretClient = new SecretClient(vaultUrl, credential);
```

## Secrets Operations

### Create/Set Secret

```typescript
const secret = await secretClient.setSecret("MySecret", "secret-value");

// With attributes
const secretWithAttrs = await secretClient.setSecret("MySecret", "value", {
  enabled: true,
  expiresOn: new Date("2025-12-31"),
  contentType: "application/json",
  tags: { environment: "production" }
});
```

### Get Secret

```typescript
// Get latest version
const secret = await secretClient.getSecret("MySecret");
console.log(secret.value);

// Get specific version
const specificSecret = await secretClient.getSecret("MySecret", {
  version: secret.properties.version
});
```

### List Secrets

```typescript
for await (const secretProperties of secretClient.listPropertiesOfSecrets()) {
  console.log(secretProperties.name);
}

// List versions
for await (const version of secretClient.listPropertiesOfSecretVersions("MySecret")) {
  console.log(version.version);
}
```

### Delete Secret

```typescript
// Soft delete
const deletePoller = await secretClient.beginDeleteSecret("MySecret");
await deletePoller.pollUntilDone();

// Purge (permanent)
await secretClient.purgeDeletedSecret("MySecret");

// Recover
const recoverPoller = await secretClient.beginRecoverDeletedSecret("MySecret");
await recoverPoller.pollUntilDone();
```

## Keys Operations

### Create Keys

```typescript
// Generic key
const key = await keyClient.createKey("MyKey", "RSA");

// RSA key with size
const rsaKey = await keyClient.createRsaKey("MyRsaKey", { keySize: 2048 });

// Elliptic Curve key
const ecKey = await keyClient.createEcKey("MyEcKey", { curve: "P-256" });

// With attributes
const keyWithAttrs = await keyClient.createKey("MyKey", "RSA", {
  enabled: true,
  expiresOn: new Date("2025-12-31"),
  tags: { purpose: "encryption" },
  keyOps: ["encrypt", "decrypt", "sign", "verify"]
});
```

### Get Key

```typescript
const key = await keyClient.getKey("MyKey");
console.log(key.name, key.keyType);
```

### List Keys

```typescript
for await (const keyProperties of keyClient.listPropertiesOfKeys()) {
  console.log(keyProperties.name);
}
```

### Rotate Key

```typescript
// Manual rotation
const rotatedKey = await keyClient.rotateKey("MyKey");

// Set rotation policy
await keyClient.updateKeyRotationPolicy("MyKey", {
  lifetimeActions: [{ action: "Rotate", timeBeforeExpiry: "P30D" }],
  expiresIn: "P90D"
});
```

### Delete Key

```typescript
const deletePoller = await keyClient.beginDeleteKey("MyKey");
await deletePoller.pollUntilDone();

// Purge
await keyClient.purgeDeletedKey("MyKey");
```

## Cryptographic Operations

### Create CryptographyClient

```typescript
import { CryptographyClient } from "@azure/keyvault-keys";

// From key object
const cryptoClient = new CryptographyClient(key, credential);

// From key ID
const cryptoClient = new CryptographyClient(key.id!, credential);
```

### Encrypt/Decrypt

```typescript
// Encrypt
const encryptResult = await cryptoClient.encrypt({
  algorithm: "RSA-OAEP",
  plaintext: Buffer.from("My secret message")
});

// Decrypt
const decryptResult = await cryptoClient.decrypt({
  algorithm: "RSA-OAEP",
  ciphertext: encryptResult.result
});

console.log(decryptResult.result.toString());
```

### Sign/Verify

```typescript
import { createHash } from "node:crypto";

// Create digest
const hash = createHash("sha256").update("My message").digest();

// Sign
const signResult = await cryptoClient.sign("RS256", hash);

// Verify
const verifyResult = await cryptoClient.verify("RS256", hash, signResult.result);
console.log("Valid:", verifyResult.result);
```

### Wrap/Unwrap Keys

```typescript
// Wrap a key (encrypt it for storage)
const wrapResult = await cryptoClient.wrapKey("RSA-OAEP", Buffer.from("key-material"));

// Unwrap
const unwrapResult = await cryptoClient.unwrapKey("RSA-OAEP", wrapResult.result);
```

## Backup and Restore

```typescript
// Backup
const keyBackup = await keyClient.backupKey("MyKey");
const secretBackup = await secretClient.backupSecret("MySecret");

// Restore (can restore to different vault)
const restoredKey = await keyClient.restoreKeyBackup(keyBackup!);
const restoredSecret = await secretClient.restoreSecretBackup(secretBackup!);
```

## Key Types

```typescript
import {
  KeyClient,
  KeyVaultKey,
  KeyProperties,
  DeletedKey,
  CryptographyClient,
  KnownEncryptionAlgorithms,
  KnownSignatureAlgorithms
} from "@azure/keyvault-keys";

import {
  SecretClient,
  KeyVaultSecret,
  SecretProperties,
  DeletedSecret
} from "@azure/keyvault-secrets";
```

## Error Handling

```typescript
try {
  const secret = await secretClient.getSecret("NonExistent");
} catch (error: any) {
  if (error.code === "SecretNotFound") {
    console.log("Secret does not exist");
  } else {
    throw error;
  }
}
```

## Best Practices

1. **Use `DefaultAzureCredential` for local development; use `ManagedIdentityCredential` or `WorkloadIdentityCredential` for production**
2. **Enable soft-delete** - Required for production vaults
3. **Set expiration dates** - On both keys and secrets
4. **Use key rotation policies** - Automate key rotation
5. **Limit key operations** - Only grant needed operations (encrypt, sign, etc.)
6. **Browser not supported** - These SDKs are Node.js only

````


### `references/keys.md`

````markdown
# Keys Reference

Cryptographic key management and operations using @azure/keyvault-keys SDK.

## Overview

The Key Vault Keys SDK provides two main clients:
- **KeyClient** - CRUD operations for keys (create, get, list, rotate, delete)
- **CryptographyClient** - Cryptographic operations using keys (encrypt, decrypt, sign, verify, wrap, unwrap)

## Core Types

```typescript
import {
  KeyClient,
  CryptographyClient,
  KeyVaultKey,
  KeyProperties,
  DeletedKey,
  KeyRotationPolicy,
  KeyRotationPolicyProperties,
  KeyRotationLifetimeAction,
  CreateKeyOptions,
  CreateRsaKeyOptions,
  CreateEcKeyOptions,
  EncryptParameters,
  DecryptParameters,
  SignResult,
  VerifyResult,
  WrapResult,
  UnwrapResult,
  KnownEncryptionAlgorithms,
  KnownSignatureAlgorithms,
  KnownKeyTypes,
  KnownKeyCurveNames
} from "@azure/keyvault-keys";
```

## KeyClient Initialization

```typescript
import { KeyClient } from "@azure/keyvault-keys";
import { DefaultAzureCredential } from "@azure/identity";

const vaultUrl = `https://${process.env.AZURE_KEYVAULT_NAME}.vault.azure.net`;
const credential = new DefaultAzureCredential();

const keyClient = new KeyClient(vaultUrl, credential);
```

## Creating Keys

### RSA Keys

```typescript
// Basic RSA key (default 2048-bit)
const rsaKey = await keyClient.createRsaKey("my-rsa-key");

// RSA key with specific size
const rsaKey2048 = await keyClient.createRsaKey("my-rsa-2048", {
  keySize: 2048
});

const rsaKey4096 = await keyClient.createRsaKey("my-rsa-4096", {
  keySize: 4096
});

// RSA-HSM (Hardware Security Module backed)
const rsaHsmKey = await keyClient.createRsaKey("my-rsa-hsm", {
  keySize: 2048,
  hsm: true  // Requires Premium vault
});
```

### Elliptic Curve Keys

```typescript
// P-256 curve (default)
const ecKey = await keyClient.createEcKey("my-ec-key");

// Specific curves
const ecKeyP256 = await keyClient.createEcKey("my-ec-p256", {
  curve: "P-256"
});

const ecKeyP384 = await keyClient.createEcKey("my-ec-p384", {
  curve: "P-384"
});

const ecKeyP521 = await keyClient.createEcKey("my-ec-p521", {
  curve: "P-521"
});

// EC-HSM
const ecHsmKey = await keyClient.createEcKey("my-ec-hsm", {
  curve: "P-256",
  hsm: true
});
```

### Oct Keys (Symmetric)

```typescript
// Symmetric key for wrap/unwrap operations
const octKey = await keyClient.createOctKey("my-oct-key", {
  keySize: 256  // 128, 192, or 256 bits
});

// Oct-HSM
const octHsmKey = await keyClient.createOctKey("my-oct-hsm", {
  keySize: 256,
  hsm: true
});
```

### Generic Create with Options

```typescript
const key = await keyClient.createKey("my-key", "RSA", {
  keySize: 2048,
  enabled: true,
  expiresOn: new Date("2025-12-31"),
  notBefore: new Date("2024-01-01"),
  tags: {
    environment: "production",
    application: "my-app"
  },
  keyOps: ["encrypt", "decrypt", "sign", "verify", "wrapKey", "unwrapKey"],
  exportable: false,
  releasePolicy: undefined  // For Managed HSM key release
});
```

## Key Operations

### Get Key

```typescript
// Get latest version
const key = await keyClient.getKey("my-key");
console.log(`Key: ${key.name}, Type: ${key.keyType}, ID: ${key.id}`);

// Get specific version
const keyVersion = await keyClient.getKey("my-key", {
  version: "abc123..."
});
```

### List Keys

```typescript
// List all keys (properties only, not key material)
for await (const keyProperties of keyClient.listPropertiesOfKeys()) {
  console.log(`Key: ${keyProperties.name}, Created: ${keyProperties.createdOn}`);
}

// List all versions of a key
for await (const version of keyClient.listPropertiesOfKeyVersions("my-key")) {
  console.log(`Version: ${version.version}, Enabled: ${version.enabled}`);
}

// List deleted keys (soft-delete enabled vaults)
for await (const deletedKey of keyClient.listDeletedKeys()) {
  console.log(`Deleted: ${deletedKey.name}, Scheduled purge: ${deletedKey.scheduledPurgeDate}`);
}
```

### Update Key Properties

```typescript
const updated = await keyClient.updateKeyProperties("my-key", {
  enabled: false,
  expiresOn: new Date("2026-12-31"),
  tags: { status: "deprecated" }
});

// Update specific version
const updatedVersion = await keyClient.updateKeyProperties("my-key", "version-id", {
  enabled: true
});
```

### Import Key

```typescript
import { JsonWebKey } from "@azure/keyvault-keys";

// Import existing key material
const jwk: JsonWebKey = {
  kty: "RSA",
  n: Buffer.from("...modulus..."),
  e: Buffer.from("...exponent..."),
  d: Buffer.from("...private exponent..."),  // Optional for public key
  // ... other RSA parameters
};

const importedKey = await keyClient.importKey("imported-key", jwk, {
  hardwareProtected: false  // true for HSM
});
```

## Key Rotation

### Manual Rotation

```typescript
// Creates new version, previous versions remain valid
const rotatedKey = await keyClient.rotateKey("my-key");
console.log(`New version: ${rotatedKey.properties.version}`);
```

### Rotation Policy

```typescript
// Get current policy
const policy = await keyClient.getKeyRotationPolicy("my-key");

// Update rotation policy
const updatedPolicy = await keyClient.updateKeyRotationPolicy("my-key", {
  expiresIn: "P90D",  // ISO 8601 duration - key expires 90 days after creation
  lifetimeActions: [
    {
      action: "Rotate",
      timeAfterCreate: "P30D"  // Auto-rotate 30 days after creation
    },
    {
      action: "Notify",
      timeBeforeExpiry: "P7D"  // Notify 7 days before expiry
    }
  ]
});

// Rotation policy with multiple actions
const complexPolicy = await keyClient.updateKeyRotationPolicy("my-key", {
  expiresIn: "P1Y",  // 1 year
  lifetimeActions: [
    { action: "Rotate", timeAfterCreate: "P90D" },  // Rotate every 90 days
    { action: "Notify", timeBeforeExpiry: "P30D" }  // Notify 30 days before expiry
  ]
});
```

### ISO 8601 Duration Format

| Duration | Meaning |
|----------|---------|
| `P30D` | 30 days |
| `P90D` | 90 days |
| `P1Y` | 1 year |
| `P6M` | 6 months |
| `P1Y6M` | 1 year 6 months |

## Key Deletion and Recovery

### Soft Delete (Default)

```typescript
// Begin delete (returns poller for long-running operation)
const deletePoller = await keyClient.beginDeleteKey("my-key");

// Wait for deletion to complete
const deletedKey = await deletePoller.pollUntilDone();
console.log(`Deleted: ${deletedKey.name}, Recovery ID: ${deletedKey.recoveryId}`);

// Get deleted key info
const deleted = await keyClient.getDeletedKey("my-key");

// Recover deleted key
const recoverPoller = await keyClient.beginRecoverDeletedKey("my-key");
const recoveredKey = await recoverPoller.pollUntilDone();

// Permanently delete (purge) - irreversible
await keyClient.purgeDeletedKey("my-key");
```

### Immediate Deletion (Non-blocking)

```typescript
// Start deletion without waiting
const poller = await keyClient.beginDeleteKey("my-key");

// Check status periodically
while (!poller.isDone()) {
  await poller.poll();
  console.log(`State: ${poller.getOperationState().status}`);
  await new Promise(resolve => setTimeout(resolve, 1000));
}
```

## CryptographyClient

### Initialization

```typescript
import { CryptographyClient } from "@azure/keyvault-keys";

// From KeyVaultKey object
const key = await keyClient.getKey("my-key");
const cryptoClient = new CryptographyClient(key, credential);

// From key ID (URL)
const cryptoClientFromId = new CryptographyClient(
  "https://my-vault.vault.azure.net/keys/my-key/version",
  credential
);

// From key ID without version (uses latest)
const cryptoClientLatest = new CryptographyClient(
  "https://my-vault.vault.azure.net/keys/my-key",
  credential
);
```

### Encrypt / Decrypt

```typescript
// RSA encryption
const plaintext = Buffer.from("Secret message");

// Encrypt with RSA-OAEP
const encryptResult = await cryptoClient.encrypt({
  algorithm: "RSA-OAEP",
  plaintext
});

console.log(`Encrypted (${encryptResult.result.length} bytes)`);

// Decrypt
const decryptResult = await cryptoClient.decrypt({
  algorithm: "RSA-OAEP",
  ciphertext: encryptResult.result
});

console.log(`Decrypted: ${decryptResult.result.toString()}`);
```

### Encryption Algorithms

| Algorithm | Key Type | Description |
|-----------|----------|-------------|
| `RSA1_5` | RSA | RSA with PKCS#1 v1.5 padding |
| `RSA-OAEP` | RSA | RSA with OAEP padding (SHA-1) |
| `RSA-OAEP-256` | RSA | RSA with OAEP padding (SHA-256) |
| `A128GCM` | oct | AES-128-GCM |
| `A192GCM` | oct | AES-192-GCM |
| `A256GCM` | oct | AES-256-GCM |
| `A128CBC` | oct | AES-128-CBC |
| `A192CBC` | oct | AES-192-CBC |
| `A256CBC` | oct | AES-256-CBC |

### Sign / Verify

```typescript
import { createHash } from "node:crypto";

// Create SHA-256 hash of data to sign
const data = Buffer.from("Data to sign");
const hash = createHash("sha256").update(data).digest();

// Sign with RSA key
const signResult = await cryptoClient.sign("RS256", hash);
console.log(`Signature (${signResult.result.length} bytes)`);

// Verify signature
const verifyResult = await cryptoClient.verify("RS256", hash, signResult.result);
console.log(`Valid: ${verifyResult.result}`);

// Sign data directly (SDK computes hash)
const signDataResult = await cryptoClient.signData("RS256", data);
const verifyDataResult = await cryptoClient.verifyData("RS256", data, signDataResult.result);
```

### Signature Algorithms

| Algorithm | Key Type | Hash | Description |
|-----------|----------|------|-------------|
| `RS256` | RSA | SHA-256 | RSASSA-PKCS1-v1_5 |
| `RS384` | RSA | SHA-384 | RSASSA-PKCS1-v1_5 |
| `RS512` | RSA | SHA-512 | RSASSA-PKCS1-v1_5 |
| `PS256` | RSA | SHA-256 | RSASSA-PSS |
| `PS384` | RSA | SHA-384 | RSASSA-PSS |
| `PS512` | RSA | SHA-512 | RSASSA-PSS |
| `ES256` | EC P-256 | SHA-256 | ECDSA |
| `ES384` | EC P-384 | SHA-384 | ECDSA |
| `ES512` | EC P-521 | SHA-512 | ECDSA |

### Wrap / Unwrap Keys

```typescript
// Key encryption key (KEK) wraps another key
const keyMaterial = Buffer.from("32-byte-key-material-here!!!!!");  // 32 bytes for AES-256

// Wrap (encrypt) the key material
const wrapResult = await cryptoClient.wrapKey("RSA-OAEP", keyMaterial);
console.log(`Wrapped key (${wrapResult.result.length} bytes)`);

// Unwrap (decrypt) the key material
const unwrapResult = await cryptoClient.unwrapKey("RSA-OAEP", wrapResult.result);
console.log(`Unwrapped: ${unwrapResult.result.length} bytes`);
```

## Backup and Restore

```typescript
// Backup key (returns encrypted blob)
const backup = await keyClient.backupKey("my-key");
if (backup) {
  // Store backup securely (e.g., blob storage)
  console.log(`Backup size: ${backup.length} bytes`);
}

// Restore key (can restore to different vault in same region/subscription)
const restoredKey = await keyClient.restoreKeyBackup(backup!);
console.log(`Restored: ${restoredKey.name}`);
```

## Error Handling

```typescript
import { RestError } from "@azure/core-rest-pipeline";

try {
  const key = await keyClient.getKey("non-existent-key");
} catch (error) {
  if (error instanceof RestError) {
    switch (error.statusCode) {
      case 404:
        console.log("Key not found");
        break;
      case 403:
        console.log("Access denied - check RBAC permissions");
        break;
      case 409:
        console.log("Conflict - key already exists or is being deleted");
        break;
      default:
        console.log(`Error ${error.statusCode}: ${error.message}`);
    }
  }
  throw error;
}
```

## Best Practices

1. **Use managed identity in production** - DefaultAzureCredential handles this automatically
2. **Enable soft-delete and purge protection** - Required for production vaults
3. **Set key expiration** - Use `expiresOn` to enforce key lifecycle
4. **Use rotation policies** - Automate key rotation for security compliance
5. **Limit key operations** - Only grant needed operations (`keyOps`)
6. **Use HSM for sensitive keys** - Hardware protection for critical cryptographic material
7. **Backup keys before deletion** - Soft-delete has retention limits
8. **Use specific key versions** - Pin to versions in production for stability

## See Also

- [secrets.md](./secrets.md) - Secret management operations

````


### `references/secrets.md`

````markdown
# Secrets Reference

Secret management operations using @azure/keyvault-secrets SDK.

## Overview

The SecretClient provides operations for managing secrets in Azure Key Vault:
- Create, update, and delete secrets
- List secrets and versions
- Soft-delete and purge operations
- Backup and restore capabilities

## Core Types

```typescript
import {
  SecretClient,
  KeyVaultSecret,
  SecretProperties,
  DeletedSecret,
  SetSecretOptions,
  GetSecretOptions,
  UpdateSecretPropertiesOptions,
  BeginDeleteSecretOptions,
  BeginRecoverDeletedSecretOptions,
  ListPropertiesOfSecretsOptions,
  ListPropertiesOfSecretVersionsOptions,
  ListDeletedSecretsOptions
} from "@azure/keyvault-secrets";
```

## SecretClient Initialization

```typescript
import { SecretClient } from "@azure/keyvault-secrets";
import { DefaultAzureCredential } from "@azure/identity";

const vaultUrl = `https://${process.env.AZURE_KEYVAULT_NAME}.vault.azure.net`;
const credential = new DefaultAzureCredential();

const secretClient = new SecretClient(vaultUrl, credential);
```

## Creating and Updating Secrets

### Set Secret (Create or Update)

```typescript
// Basic secret
const secret = await secretClient.setSecret("MySecret", "secret-value");
console.log(`Secret: ${secret.name}, Version: ${secret.properties.version}`);

// Secret with options
const secretWithOptions = await secretClient.setSecret("MySecret", "secret-value", {
  enabled: true,
  expiresOn: new Date("2025-12-31"),
  notBefore: new Date("2024-01-01"),
  contentType: "text/plain",
  tags: {
    environment: "production",
    application: "my-app",
    owner: "team-a"
  }
});
```

### Content Types

```typescript
// JSON content
const jsonSecret = await secretClient.setSecret(
  "config-secret",
  JSON.stringify({ apiKey: "xyz", endpoint: "https://api.example.com" }),
  { contentType: "application/json" }
);

// Connection string
const connString = await secretClient.setSecret(
  "db-connection",
  "Server=tcp:myserver.database.windows.net;Database=mydb;...",
  { contentType: "text/plain; charset=utf-8" }
);

// Base64 encoded binary
const binarySecret = await secretClient.setSecret(
  "certificate-data",
  Buffer.from(certificateBytes).toString("base64"),
  { contentType: "application/x-pkcs12" }
);
```

### Versioning Behavior

```typescript
// Each setSecret creates a new version
const v1 = await secretClient.setSecret("MySecret", "value-1");
console.log(`Version 1: ${v1.properties.version}`);

const v2 = await secretClient.setSecret("MySecret", "value-2");
console.log(`Version 2: ${v2.properties.version}`);

// v1 still exists and is accessible by version ID
const v1Retrieved = await secretClient.getSecret("MySecret", {
  version: v1.properties.version
});
```

## Retrieving Secrets

### Get Secret

```typescript
// Get latest version
const secret = await secretClient.getSecret("MySecret");
console.log(`Value: ${secret.value}`);
console.log(`Version: ${secret.properties.version}`);
console.log(`Created: ${secret.properties.createdOn}`);

// Get specific version
const specificVersion = await secretClient.getSecret("MySecret", {
  version: "abc123def456..."
});
```

### KeyVaultSecret Structure

```typescript
interface KeyVaultSecret {
  name: string;
  value?: string;  // Only present when retrieved, not in list operations
  properties: SecretProperties;
}

interface SecretProperties {
  id?: string;                    // Full secret identifier URL
  name: string;
  version?: string;
  vaultUrl: string;
  enabled?: boolean;
  notBefore?: Date;
  expiresOn?: Date;
  createdOn?: Date;
  updatedOn?: Date;
  contentType?: string;
  tags?: { [key: string]: string };
  managed?: boolean;              // True if managed by Key Vault (e.g., storage account keys)
  recoverableDays?: number;
  recoveryLevel?: string;
}
```

## Listing Secrets

### List All Secrets

```typescript
// List secret properties (not values - use getSecret for values)
for await (const secretProperties of secretClient.listPropertiesOfSecrets()) {
  console.log(`Secret: ${secretProperties.name}`);
  console.log(`  Enabled: ${secretProperties.enabled}`);
  console.log(`  Content Type: ${secretProperties.contentType}`);
  console.log(`  Tags: ${JSON.stringify(secretProperties.tags)}`);
}
```

### List Secret Versions

```typescript
// List all versions of a specific secret
for await (const version of secretClient.listPropertiesOfSecretVersions("MySecret")) {
  console.log(`Version: ${version.version}`);
  console.log(`  Created: ${version.createdOn}`);
  console.log(`  Enabled: ${version.enabled}`);
  console.log(`  Expires: ${version.expiresOn}`);
}
```

### Collect to Array

```typescript
// Collect all secrets to array
const allSecrets: SecretProperties[] = [];
for await (const secret of secretClient.listPropertiesOfSecrets()) {
  allSecrets.push(secret);
}

// Or use byPage for pagination control
const pages = secretClient.listPropertiesOfSecrets().byPage({ maxPageSize: 25 });
for await (const page of pages) {
  console.log(`Page with ${page.length} secrets`);
}
```

### Filter by Tags

```typescript
// SDK doesn't support server-side filtering, filter client-side
const productionSecrets: SecretProperties[] = [];
for await (const secret of secretClient.listPropertiesOfSecrets()) {
  if (secret.tags?.environment === "production") {
    productionSecrets.push(secret);
  }
}
```

## Updating Secret Properties

```typescript
// Update properties without changing value
const updated = await secretClient.updateSecretProperties("MySecret", "version-id", {
  enabled: false,
  expiresOn: new Date("2026-12-31"),
  tags: { status: "deprecated", deprecatedOn: new Date().toISOString() }
});

// Update latest version (get version first)
const current = await secretClient.getSecret("MySecret");
await secretClient.updateSecretProperties("MySecret", current.properties.version!, {
  enabled: true
});
```

## Soft Delete Operations

### Delete Secret

```typescript
// Begin delete (long-running operation)
const deletePoller = await secretClient.beginDeleteSecret("MySecret");

// Option 1: Wait for completion
const deletedSecret = await deletePoller.pollUntilDone();
console.log(`Deleted: ${deletedSecret.name}`);
console.log(`Scheduled purge: ${deletedSecret.scheduledPurgeDate}`);
console.log(`Deleted on: ${deletedSecret.deletedOn}`);

// Option 2: Non-blocking with periodic checks
const poller = await secretClient.beginDeleteSecret("MySecret");
while (!poller.isDone()) {
  await poller.poll();
  const state = poller.getOperationState();
  console.log(`Delete status: ${state.isStarted ? "in progress" : "pending"}`);
  await new Promise(resolve => setTimeout(resolve, 2000));
}
```

### Get Deleted Secret

```typescript
// Get info about a deleted secret
const deleted = await secretClient.getDeletedSecret("MySecret");
console.log(`Recovery ID: ${deleted.recoveryId}`);
console.log(`Scheduled purge: ${deleted.scheduledPurgeDate}`);
```

### List Deleted Secrets

```typescript
// List all deleted secrets in vault
for await (const deletedSecret of secretClient.listDeletedSecrets()) {
  console.log(`Deleted secret: ${deletedSecret.name}`);
  console.log(`  Deleted on: ${deletedSecret.deletedOn}`);
  console.log(`  Purge date: ${deletedSecret.scheduledPurgeDate}`);
}
```

### Recover Deleted Secret

```typescript
// Recover a soft-deleted secret
const recoverPoller = await secretClient.beginRecoverDeletedSecret("MySecret");
const recoveredSecret = await recoverPoller.pollUntilDone();
console.log(`Recovered: ${recoveredSecret.name}`);
```

### Purge Secret (Permanent Delete)

```typescript
// Permanently delete - IRREVERSIBLE
// Requires "purge" permission in RBAC
await secretClient.purgeDeletedSecret("MySecret");

// Common pattern: delete then purge
const deletePoller = await secretClient.beginDeleteSecret("MySecret");
await deletePoller.pollUntilDone();
await secretClient.purgeDeletedSecret("MySecret");
```

## Backup and Restore

### Backup Secret

```typescript
// Backup returns encrypted blob containing all versions
const backup = await secretClient.backupSecret("MySecret");

if (backup) {
  // Store backup securely (e.g., blob storage, local file)
  console.log(`Backup size: ${backup.length} bytes`);
  
  // Save to file
  import { writeFileSync } from "node:fs";
  writeFileSync("secret-backup.bin", backup);
}
```

### Restore Secret

```typescript
import { readFileSync } from "node:fs";

// Read backup from storage
const backupData = readFileSync("secret-backup.bin");

// Restore to vault (can be different vault in same region/subscription)
const restoredSecret = await secretClient.restoreSecretBackup(backupData);
console.log(`Restored: ${restoredSecret.name}`);
```

### Backup Constraints

| Constraint | Description |
|------------|-------------|
| Same subscription | Backup can only be restored to vault in same Azure subscription |
| Same geography | Target vault must be in same Azure geography |
| All versions | Backup includes all versions of the secret |
| Encrypted | Backup blob is encrypted with Microsoft-managed keys |

## Error Handling

```typescript
import { RestError } from "@azure/core-rest-pipeline";

async function getSecretSafely(name: string): Promise<KeyVaultSecret | null> {
  try {
    return await secretClient.getSecret(name);
  } catch (error) {
    if (error instanceof RestError) {
      switch (error.statusCode) {
        case 404:
          console.log(`Secret '${name}' not found`);
          return null;
        case 403:
          console.log("Access denied - check RBAC permissions");
          throw error;
        case 409:
          console.log("Conflict - secret is being deleted or already exists");
          throw error;
        default:
          console.log(`Error ${error.statusCode}: ${error.message}`);
          throw error;
      }
    }
    throw error;
  }
}
```

### Common Error Codes

| Code | Meaning |
|------|---------|
| 404 | Secret not found (or deleted) |
| 403 | Access denied (RBAC permission missing) |
| 409 | Conflict (secret exists in deleted state) |
| 429 | Rate limited (too many requests) |

## Common Patterns

### Secret Rotation

```typescript
async function rotateSecret(name: string, newValue: string): Promise<KeyVaultSecret> {
  // Get current secret to preserve metadata
  const current = await secretClient.getSecret(name);
  
  // Disable old version
  await secretClient.updateSecretProperties(name, current.properties.version!, {
    enabled: false,
    tags: {
      ...current.properties.tags,
      rotatedOn: new Date().toISOString(),
      status: "rotated"
    }
  });
  
  // Create new version with same settings
  const newSecret = await secretClient.setSecret(name, newValue, {
    enabled: true,
    contentType: current.properties.contentType,
    expiresOn: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000), // 90 days
    tags: {
      ...current.properties.tags,
      status: "active",
      createdOn: new Date().toISOString()
    }
  });
  
  return newSecret;
}
```

### Bulk Secret Operations

```typescript
async function exportAllSecrets(): Promise<Map<string, string>> {
  const secrets = new Map<string, string>();
  
  for await (const properties of secretClient.listPropertiesOfSecrets()) {
    if (properties.enabled) {
      const secret = await secretClient.getSecret(properties.name);
      if (secret.value) {
        secrets.set(properties.name, secret.value);
      }
    }
  }
  
  return secrets;
}

async function importSecrets(secrets: Map<string, string>, tags?: Record<string, string>): Promise<void> {
  for (const [name, value] of secrets) {
    await secretClient.setSecret(name, value, { tags });
    console.log(`Imported: ${name}`);
  }
}
```

### Check Secret Expiration

```typescript
async function getExpiringSecrets(daysThreshold: number = 30): Promise<SecretProperties[]> {
  const expiringSecrets: SecretProperties[] = [];
  const thresholdDate = new Date(Date.now() + daysThreshold * 24 * 60 * 60 * 1000);
  
  for await (const secret of secretClient.listPropertiesOfSecrets()) {
    if (secret.enabled && secret.expiresOn && secret.expiresOn <= thresholdDate) {
      expiringSecrets.push(secret);
    }
  }
  
  return expiringSecrets;
}
```

## Best Practices

1. **Use managed identity** - DefaultAzureCredential handles MI in Azure, dev credentials locally
2. **Set expiration dates** - Enforce secret rotation with `expiresOn`
3. **Use content types** - Helps consumers understand secret format
4. **Tag secrets** - Environment, application, owner for organization
5. **Enable soft-delete** - Required for production vaults (default for new vaults)
6. **Enable purge protection** - Prevents accidental permanent deletion
7. **Backup before rotation** - Backup secrets before making changes
8. **Least privilege** - Grant only needed permissions (Get vs List vs Set)
9. **Monitor expiration** - Alert on secrets expiring within threshold
10. **Avoid storing in code** - Use Key Vault references in App Service/Functions

## RBAC Permissions

| Operation | Required Permission |
|-----------|---------------------|
| Get secret | `Microsoft.KeyVault/vaults/secrets/getSecret/action` |
| List secrets | `Microsoft.KeyVault/vaults/secrets/readMetadata/action` |
| Set secret | `Microsoft.KeyVault/vaults/secrets/setSecret/action` |
| Delete secret | `Microsoft.KeyVault/vaults/secrets/delete` |
| Purge secret | `Microsoft.KeyVault/vaults/secrets/purge/action` |
| Backup | `Microsoft.KeyVault/vaults/secrets/backup/action` |
| Restore | `Microsoft.KeyVault/vaults/secrets/restore/action` |

## See Also

- [keys.md](./keys.md) - Cryptographic key management

````
