# SkillPatch skill: azure-keyvault-keys-ts

This skill guides agents in managing cryptographic keys using the Azure Key Vault Keys SDK for JavaScript/TypeScript (@azure/keyvault-keys). It covers authentication, key creation (RSA, EC), retrieval, listing, rotation, and deletion workflows. It also includes supplementary Azure Key Vault Secrets operations as reference examples.

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-keys-ts
curl -sSL https://skillpatch.dev/install_skill/azure-keyvault-keys-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-keys-ts
description: Manage cryptographic keys using Azure Key Vault Keys SDK for JavaScript (@azure/keyvault-keys). Use when creating, encrypting/decrypting, signing, or rotating keys.
license: MIT
metadata:
  author: Microsoft
  version: "1.0.0"
  package: '@azure/keyvault-keys'
---

# Azure Key Vault Keys SDK for TypeScript

Manage cryptographic keys with Azure Key Vault.

## Installation

```bash
# Keys SDK
npm install @azure/keyvault-keys @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 { KeyClient, CryptographyClient } from "@azure/keyvault-keys";

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

````
