refactor: flatten Microsoft skills from nested to flat directory structure
Rewrote sync_microsoft_skills.py (v4) to use each SKILL.md's frontmatter 'name' field as the flat directory name under skills/, replacing the nested skills/official/microsoft/<lang>/<category>/<service>/ hierarchy. This fixes CI failures caused by the indexing, validation, and catalog scripts expecting skills/<id>/SKILL.md (depth 1). Changes: - Rewrite scripts/sync_microsoft_skills.py for flat output with collision detection - Update scripts/tests/inspect_microsoft_repo.py for flat name mapping - Update scripts/tests/test_comprehensive_coverage.py for name uniqueness checks - Delete skills/official/ nested directory - Add 129 Microsoft skills as flat directories (e.g. skills/azure-mgmt-botservice-dotnet/) - Move attribution files to docs/ (LICENSE-MICROSOFT, microsoft-skills-attribution.json) - Rebuild skills_index.json, CATALOG.md, README.md (845 total skills)
This commit is contained in:
356
skills/azure-security-keyvault-secrets-java/SKILL.md
Normal file
356
skills/azure-security-keyvault-secrets-java/SKILL.md
Normal file
@@ -0,0 +1,356 @@
|
||||
---
|
||||
name: azure-security-keyvault-secrets-java
|
||||
description: Azure Key Vault Secrets Java SDK for secret management. Use when storing, retrieving, or managing passwords, API keys, connection strings, or other sensitive configuration data.
|
||||
package: com.azure:azure-security-keyvault-secrets
|
||||
---
|
||||
|
||||
# Azure Key Vault Secrets (Java)
|
||||
|
||||
Securely store and manage secrets like passwords, API keys, and connection strings.
|
||||
|
||||
## Installation
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.azure</groupId>
|
||||
<artifactId>azure-security-keyvault-secrets</artifactId>
|
||||
<version>4.9.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## Client Creation
|
||||
|
||||
```java
|
||||
import com.azure.security.keyvault.secrets.SecretClient;
|
||||
import com.azure.security.keyvault.secrets.SecretClientBuilder;
|
||||
import com.azure.identity.DefaultAzureCredentialBuilder;
|
||||
|
||||
// Sync client
|
||||
SecretClient secretClient = new SecretClientBuilder()
|
||||
.vaultUrl("https://<vault-name>.vault.azure.net")
|
||||
.credential(new DefaultAzureCredentialBuilder().build())
|
||||
.buildClient();
|
||||
|
||||
// Async client
|
||||
SecretAsyncClient secretAsyncClient = new SecretClientBuilder()
|
||||
.vaultUrl("https://<vault-name>.vault.azure.net")
|
||||
.credential(new DefaultAzureCredentialBuilder().build())
|
||||
.buildAsyncClient();
|
||||
```
|
||||
|
||||
## Create/Set Secret
|
||||
|
||||
```java
|
||||
import com.azure.security.keyvault.secrets.models.KeyVaultSecret;
|
||||
|
||||
// Simple secret
|
||||
KeyVaultSecret secret = secretClient.setSecret("database-password", "P@ssw0rd123!");
|
||||
System.out.println("Secret name: " + secret.getName());
|
||||
System.out.println("Secret ID: " + secret.getId());
|
||||
|
||||
// Secret with options
|
||||
KeyVaultSecret secretWithOptions = secretClient.setSecret(
|
||||
new KeyVaultSecret("api-key", "sk_live_abc123xyz")
|
||||
.setProperties(new SecretProperties()
|
||||
.setContentType("application/json")
|
||||
.setExpiresOn(OffsetDateTime.now().plusYears(1))
|
||||
.setNotBefore(OffsetDateTime.now())
|
||||
.setEnabled(true)
|
||||
.setTags(Map.of(
|
||||
"environment", "production",
|
||||
"service", "payment-api"
|
||||
))
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
## Get Secret
|
||||
|
||||
```java
|
||||
// Get latest version
|
||||
KeyVaultSecret secret = secretClient.getSecret("database-password");
|
||||
String value = secret.getValue();
|
||||
System.out.println("Secret value: " + value);
|
||||
|
||||
// Get specific version
|
||||
KeyVaultSecret specificVersion = secretClient.getSecret("database-password", "<version-id>");
|
||||
|
||||
// Get only properties (no value)
|
||||
SecretProperties props = secretClient.getSecret("database-password").getProperties();
|
||||
System.out.println("Enabled: " + props.isEnabled());
|
||||
System.out.println("Created: " + props.getCreatedOn());
|
||||
```
|
||||
|
||||
## Update Secret Properties
|
||||
|
||||
```java
|
||||
// Get secret
|
||||
KeyVaultSecret secret = secretClient.getSecret("api-key");
|
||||
|
||||
// Update properties (cannot update value - create new version instead)
|
||||
secret.getProperties()
|
||||
.setEnabled(false)
|
||||
.setExpiresOn(OffsetDateTime.now().plusMonths(6))
|
||||
.setTags(Map.of("status", "rotating"));
|
||||
|
||||
SecretProperties updated = secretClient.updateSecretProperties(secret.getProperties());
|
||||
System.out.println("Updated: " + updated.getUpdatedOn());
|
||||
```
|
||||
|
||||
## List Secrets
|
||||
|
||||
```java
|
||||
import com.azure.core.util.paging.PagedIterable;
|
||||
import com.azure.security.keyvault.secrets.models.SecretProperties;
|
||||
|
||||
// List all secrets (properties only, no values)
|
||||
for (SecretProperties secretProps : secretClient.listPropertiesOfSecrets()) {
|
||||
System.out.println("Secret: " + secretProps.getName());
|
||||
System.out.println(" Enabled: " + secretProps.isEnabled());
|
||||
System.out.println(" Created: " + secretProps.getCreatedOn());
|
||||
System.out.println(" Content-Type: " + secretProps.getContentType());
|
||||
|
||||
// Get value if needed
|
||||
if (secretProps.isEnabled()) {
|
||||
KeyVaultSecret fullSecret = secretClient.getSecret(secretProps.getName());
|
||||
System.out.println(" Value: " + fullSecret.getValue().substring(0, 5) + "...");
|
||||
}
|
||||
}
|
||||
|
||||
// List versions of a secret
|
||||
for (SecretProperties version : secretClient.listPropertiesOfSecretVersions("database-password")) {
|
||||
System.out.println("Version: " + version.getVersion());
|
||||
System.out.println("Created: " + version.getCreatedOn());
|
||||
System.out.println("Enabled: " + version.isEnabled());
|
||||
}
|
||||
```
|
||||
|
||||
## Delete Secret
|
||||
|
||||
```java
|
||||
import com.azure.core.util.polling.SyncPoller;
|
||||
import com.azure.security.keyvault.secrets.models.DeletedSecret;
|
||||
|
||||
// Begin delete (returns poller for soft-delete enabled vaults)
|
||||
SyncPoller<DeletedSecret, Void> deletePoller = secretClient.beginDeleteSecret("old-secret");
|
||||
|
||||
// Wait for deletion
|
||||
DeletedSecret deletedSecret = deletePoller.poll().getValue();
|
||||
System.out.println("Deleted on: " + deletedSecret.getDeletedOn());
|
||||
System.out.println("Scheduled purge: " + deletedSecret.getScheduledPurgeDate());
|
||||
|
||||
deletePoller.waitForCompletion();
|
||||
```
|
||||
|
||||
## Recover Deleted Secret
|
||||
|
||||
```java
|
||||
// List deleted secrets
|
||||
for (DeletedSecret deleted : secretClient.listDeletedSecrets()) {
|
||||
System.out.println("Deleted: " + deleted.getName());
|
||||
System.out.println("Deletion date: " + deleted.getDeletedOn());
|
||||
}
|
||||
|
||||
// Recover deleted secret
|
||||
SyncPoller<KeyVaultSecret, Void> recoverPoller = secretClient.beginRecoverDeletedSecret("old-secret");
|
||||
recoverPoller.waitForCompletion();
|
||||
|
||||
KeyVaultSecret recovered = recoverPoller.getFinalResult();
|
||||
System.out.println("Recovered: " + recovered.getName());
|
||||
```
|
||||
|
||||
## Purge Deleted Secret
|
||||
|
||||
```java
|
||||
// Permanently delete (cannot be recovered)
|
||||
secretClient.purgeDeletedSecret("old-secret");
|
||||
|
||||
// Get deleted secret info first
|
||||
DeletedSecret deleted = secretClient.getDeletedSecret("old-secret");
|
||||
System.out.println("Will purge: " + deleted.getName());
|
||||
secretClient.purgeDeletedSecret("old-secret");
|
||||
```
|
||||
|
||||
## Backup and Restore
|
||||
|
||||
```java
|
||||
// Backup secret (all versions)
|
||||
byte[] backup = secretClient.backupSecret("important-secret");
|
||||
|
||||
// Save to file
|
||||
Files.write(Paths.get("secret-backup.blob"), backup);
|
||||
|
||||
// Restore from backup
|
||||
byte[] backupData = Files.readAllBytes(Paths.get("secret-backup.blob"));
|
||||
KeyVaultSecret restored = secretClient.restoreSecretBackup(backupData);
|
||||
System.out.println("Restored: " + restored.getName());
|
||||
```
|
||||
|
||||
## Async Operations
|
||||
|
||||
```java
|
||||
SecretAsyncClient asyncClient = new SecretClientBuilder()
|
||||
.vaultUrl("https://<vault>.vault.azure.net")
|
||||
.credential(new DefaultAzureCredentialBuilder().build())
|
||||
.buildAsyncClient();
|
||||
|
||||
// Set secret async
|
||||
asyncClient.setSecret("async-secret", "async-value")
|
||||
.subscribe(
|
||||
secret -> System.out.println("Created: " + secret.getName()),
|
||||
error -> System.out.println("Error: " + error.getMessage())
|
||||
);
|
||||
|
||||
// Get secret async
|
||||
asyncClient.getSecret("async-secret")
|
||||
.subscribe(secret -> System.out.println("Value: " + secret.getValue()));
|
||||
|
||||
// List secrets async
|
||||
asyncClient.listPropertiesOfSecrets()
|
||||
.doOnNext(props -> System.out.println("Found: " + props.getName()))
|
||||
.subscribe();
|
||||
```
|
||||
|
||||
## Configuration Patterns
|
||||
|
||||
### Load Multiple Secrets
|
||||
|
||||
```java
|
||||
public class ConfigLoader {
|
||||
private final SecretClient client;
|
||||
|
||||
public ConfigLoader(String vaultUrl) {
|
||||
this.client = new SecretClientBuilder()
|
||||
.vaultUrl(vaultUrl)
|
||||
.credential(new DefaultAzureCredentialBuilder().build())
|
||||
.buildClient();
|
||||
}
|
||||
|
||||
public Map<String, String> loadSecrets(List<String> secretNames) {
|
||||
Map<String, String> secrets = new HashMap<>();
|
||||
for (String name : secretNames) {
|
||||
try {
|
||||
KeyVaultSecret secret = client.getSecret(name);
|
||||
secrets.put(name, secret.getValue());
|
||||
} catch (ResourceNotFoundException e) {
|
||||
System.out.println("Secret not found: " + name);
|
||||
}
|
||||
}
|
||||
return secrets;
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
ConfigLoader loader = new ConfigLoader("https://my-vault.vault.azure.net");
|
||||
Map<String, String> config = loader.loadSecrets(
|
||||
Arrays.asList("db-connection-string", "api-key", "jwt-secret")
|
||||
);
|
||||
```
|
||||
|
||||
### Secret Rotation Pattern
|
||||
|
||||
```java
|
||||
public void rotateSecret(String secretName, String newValue) {
|
||||
// Get current secret
|
||||
KeyVaultSecret current = secretClient.getSecret(secretName);
|
||||
|
||||
// Disable old version
|
||||
current.getProperties().setEnabled(false);
|
||||
secretClient.updateSecretProperties(current.getProperties());
|
||||
|
||||
// Create new version with new value
|
||||
KeyVaultSecret newSecret = secretClient.setSecret(secretName, newValue);
|
||||
System.out.println("Rotated to version: " + newSecret.getProperties().getVersion());
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```java
|
||||
import com.azure.core.exception.HttpResponseException;
|
||||
import com.azure.core.exception.ResourceNotFoundException;
|
||||
|
||||
try {
|
||||
KeyVaultSecret secret = secretClient.getSecret("my-secret");
|
||||
System.out.println("Value: " + secret.getValue());
|
||||
} catch (ResourceNotFoundException e) {
|
||||
System.out.println("Secret not found");
|
||||
} catch (HttpResponseException e) {
|
||||
int status = e.getResponse().getStatusCode();
|
||||
if (status == 403) {
|
||||
System.out.println("Access denied - check permissions");
|
||||
} else if (status == 429) {
|
||||
System.out.println("Rate limited - retry later");
|
||||
} else {
|
||||
System.out.println("HTTP error: " + status);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Secret Properties
|
||||
|
||||
| Property | Description |
|
||||
|----------|-------------|
|
||||
| `name` | Secret name |
|
||||
| `value` | Secret value (string) |
|
||||
| `id` | Full identifier URL |
|
||||
| `contentType` | MIME type hint |
|
||||
| `enabled` | Whether secret can be retrieved |
|
||||
| `notBefore` | Activation time |
|
||||
| `expiresOn` | Expiration time |
|
||||
| `createdOn` | Creation timestamp |
|
||||
| `updatedOn` | Last update timestamp |
|
||||
| `recoveryLevel` | Soft-delete recovery level |
|
||||
| `tags` | User-defined metadata |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_KEYVAULT_URL=https://<vault-name>.vault.azure.net
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Enable Soft Delete** - Protects against accidental deletion
|
||||
2. **Use Tags** - Tag secrets with environment, service, owner
|
||||
3. **Set Expiration** - Use `setExpiresOn()` for credentials that should rotate
|
||||
4. **Content Type** - Set `contentType` to indicate format (e.g., `application/json`)
|
||||
5. **Version Management** - Don't delete old versions immediately during rotation
|
||||
6. **Access Logging** - Enable diagnostic logging on Key Vault
|
||||
7. **Least Privilege** - Use separate vaults for different environments
|
||||
|
||||
## Common Secret Types
|
||||
|
||||
```java
|
||||
// Database connection string
|
||||
secretClient.setSecret(new KeyVaultSecret("db-connection",
|
||||
"Server=myserver.database.windows.net;Database=mydb;...")
|
||||
.setProperties(new SecretProperties()
|
||||
.setContentType("text/plain")
|
||||
.setTags(Map.of("type", "connection-string"))));
|
||||
|
||||
// API key
|
||||
secretClient.setSecret(new KeyVaultSecret("stripe-api-key", "sk_live_...")
|
||||
.setProperties(new SecretProperties()
|
||||
.setContentType("text/plain")
|
||||
.setExpiresOn(OffsetDateTime.now().plusYears(1))));
|
||||
|
||||
// JSON configuration
|
||||
secretClient.setSecret(new KeyVaultSecret("app-config",
|
||||
"{\"endpoint\":\"https://...\",\"key\":\"...\"}")
|
||||
.setProperties(new SecretProperties()
|
||||
.setContentType("application/json")));
|
||||
|
||||
// Certificate password
|
||||
secretClient.setSecret(new KeyVaultSecret("cert-password", "CertP@ss!")
|
||||
.setProperties(new SecretProperties()
|
||||
.setContentType("text/plain")
|
||||
.setTags(Map.of("certificate", "my-cert"))));
|
||||
```
|
||||
|
||||
## Trigger Phrases
|
||||
|
||||
- "Key Vault secrets Java", "secret management Java"
|
||||
- "store password", "store API key", "connection string"
|
||||
- "retrieve secret", "rotate secret"
|
||||
- "Azure secrets", "vault secrets"
|
||||
Reference in New Issue
Block a user