From e2891fbf85ccde7c7e8c9c977151e4480fd981a6 Mon Sep 17 00:00:00 2001 From: webbrain-one <295484252+webbrain-one@users.noreply.github.com> Date: Sat, 15 Aug 2026 14:30:20 +0300 Subject: [PATCH] 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. --- README.md | 10 +++++-- src/main.rs | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3c4c80a..f80147d 100644 --- a/README.md +++ b/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 [--output ] [--filter ] +cargo run -- --vault-name [--output ] [--filter ] [--set] ``` ## Usage @@ -77,12 +78,13 @@ cargo run -- --vault-name [--output ] [--filter ] With the binary on your `PATH`, run Keyweave as follows: ```sh -keyweave --vault-name [--output ] [--filter ] +keyweave --vault-name [--output ] [--filter ] [--set] ``` - `--vault-name `: Sets the name of the Azure Key Vault. - `--output `: (Optional) Sets the name of the output file (default: `.env`). - `--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 [--output ] [--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). diff --git a/src/main.rs b/src/main.rs index b12608d..2171df2 100644 --- a/src/main.rs +++ b/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, + + /// 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 { + 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: {}", + opts.output + )); + let count = set_secrets_from_env_file(&client, &opts.output).await?; + log.success(format!( + "Set {} secret(s) in Key Vault: {}", + count, opts.vault_name + )); + log.success("Done."); + return Ok(()); + } + log.loading(format!( "Fetching secrets from Key Vault: {}", opts.vault_name