mirror of
https://github.com/bartvdbraak/keyweave.git
synced 2026-09-14 00:36:37 +00:00
Add --set flag to update Key Vault from .env file
Adds a boolean --set flag for updating Key Vault values from .env, implements the corresponding logic, and documents required setup and Access Policies.
This commit is contained in:
parent
9a47a2ae4f
commit
e2891fbf85
2 changed files with 82 additions and 3 deletions
10
README.md
10
README.md
|
|
@ -16,6 +16,7 @@ Keyweave is an open-source tool crafted to seamlessly fetch secrets from Azure K
|
|||
- **Filtering**: Optionally filter the secrets to be retrieved by name.
|
||||
- **Output Customization**: Choose the name of the output file, defaulting to `.env`.
|
||||
- **Azure Default Credentials**: Utilizes Azure default credentials for authentication.
|
||||
- **Set Secrets**: Push secrets from a `.env` file to Azure Key Vault with `--set`.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
|
|
@ -69,7 +70,7 @@ cargo build --release
|
|||
Once built, run Keyweave using Cargo:
|
||||
|
||||
```sh
|
||||
cargo run -- --vault-name <VAULT_NAME> [--output <FILE>] [--filter <FILTER>]
|
||||
cargo run -- --vault-name <VAULT_NAME> [--output <FILE>] [--filter <FILTER>] [--set]
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
|
@ -77,12 +78,13 @@ cargo run -- --vault-name <VAULT_NAME> [--output <FILE>] [--filter <FILTER>]
|
|||
With the binary on your `PATH`, run Keyweave as follows:
|
||||
|
||||
```sh
|
||||
keyweave --vault-name <VAULT_NAME> [--output <FILE>] [--filter <FILTER>]
|
||||
keyweave --vault-name <VAULT_NAME> [--output <FILE>] [--filter <FILTER>] [--set]
|
||||
```
|
||||
|
||||
- `--vault-name <VAULT_NAME>`: Sets the name of the Azure Key Vault.
|
||||
- `--output <FILE>`: (Optional) Sets the name of the output file (default: `.env`).
|
||||
- `--filter <FILTER>`: (Optional) Filters the secrets to be retrieved by name.
|
||||
- `-s, --set`: (Optional) Sets secrets in the Key Vault from the output file (default: `.env`). Requires `Set` Secret Permission in the Key Vault access policy.
|
||||
|
||||
### Example
|
||||
|
||||
|
|
@ -90,6 +92,10 @@ keyweave --vault-name <VAULT_NAME> [--output <FILE>] [--filter <FILTER>]
|
|||
keyweave --vault-name my-key-vault --output my-env-file.env --filter my-secret
|
||||
```
|
||||
|
||||
```sh
|
||||
keyweave --vault-name my-key-vault --set
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Additional documentation for this package can be found on [docs.rs](https://docs.rs/keyweave).
|
||||
|
|
|
|||
75
src/main.rs
75
src/main.rs
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
|||
use azure_identity::{DefaultAzureCredential, TokenCredentialOptions};
|
||||
use azure_security_keyvault::prelude::KeyVaultGetSecretsResponse;
|
||||
use azure_security_keyvault::KeyvaultClient;
|
||||
use clap::Parser;
|
||||
use clap::{ArgAction, Parser};
|
||||
use futures::stream::StreamExt;
|
||||
use paris::{error, Logger};
|
||||
use std::error::Error;
|
||||
|
|
@ -40,6 +40,10 @@ struct Opts {
|
|||
/// Filters the secrets to be retrieved by name
|
||||
#[clap(short, long, value_name = "FILTER")]
|
||||
filter: Option<String>,
|
||||
|
||||
/// Sets secrets in the Key Vault from the output file
|
||||
#[clap(short, long, action = ArgAction::SetTrue)]
|
||||
set: bool,
|
||||
}
|
||||
|
||||
async fn check_vault_dns(vault_name: &str) -> Result<()> {
|
||||
|
|
@ -199,6 +203,61 @@ fn create_env_file(secrets: Vec<(String, String)>, output_file: &str) -> Result<
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_secrets_from_env_file(client: &KeyvaultClient, input_file: &str) -> Result<u32> {
|
||||
let content = match std::fs::read_to_string(input_file) {
|
||||
Ok(c) => c,
|
||||
Err(err) => {
|
||||
error!("Failed to read input file: {}", err);
|
||||
return Err(CustomError {
|
||||
message: "An error occurred while reading the .env file".to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
};
|
||||
|
||||
let mut set_count = 0u32;
|
||||
for line in content.lines() {
|
||||
let line = line.trim();
|
||||
if line.is_empty() || line.starts_with('#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (key, value) = match line.split_once('=') {
|
||||
Some((k, v)) => (k.trim(), v.trim()),
|
||||
None => {
|
||||
error!("Invalid line in input file: {}", line);
|
||||
return Err(CustomError {
|
||||
message: "An error occurred while parsing the .env file".to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
};
|
||||
|
||||
if key.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
match client.secret_client().set(key, value).await {
|
||||
Ok(_) => {
|
||||
set_count += 1;
|
||||
}
|
||||
Err(err) => {
|
||||
error!(
|
||||
"Failed to set secret: {}. Make sure you have Set permissions on the Key Vault.",
|
||||
key
|
||||
);
|
||||
error!("Error: {}", err);
|
||||
return Err(CustomError {
|
||||
message: "An error occurred while setting secrets".to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(set_count)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
@ -254,6 +313,20 @@ async fn main() -> Result<()> {
|
|||
|
||||
check_vault_dns(&opts.vault_name).await?;
|
||||
|
||||
if opts.set {
|
||||
log.loading(format!(
|
||||
"Setting secrets from file: <blue>{}</>",
|
||||
opts.output
|
||||
));
|
||||
let count = set_secrets_from_env_file(&client, &opts.output).await?;
|
||||
log.success(format!(
|
||||
"Set {} secret(s) in Key Vault: <blue>{}</>",
|
||||
count, opts.vault_name
|
||||
));
|
||||
log.success("Done.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
log.loading(format!(
|
||||
"Fetching secrets from Key Vault: <blue>{}</>",
|
||||
opts.vault_name
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue