diff --git a/Module/PSInfisicalAPI/en-US/PSInfisicalAPI.dll-Help.xml b/Module/PSInfisicalAPI/en-US/PSInfisicalAPI.dll-Help.xml index 78b9730..cb37781 100644 --- a/Module/PSInfisicalAPI/en-US/PSInfisicalAPI.dll-Help.xml +++ b/Module/PSInfisicalAPI/en-US/PSInfisicalAPI.dll-Help.xml @@ -295,7 +295,7 @@ $CopyInfisicalSecretResult = Copy-InfisicalSecret @CopyInfisicalSecretParameters InfisicalSecretDictionary - Aggregates an incoming pipeline of InfisicalSecret objects into a case-insensitive Dictionary keyed by SecretName. By default values are SecureString; pass -AsPlainText to materialize string values. Duplicate keys are handled via the -DuplicateKeyBehavior parameter (Error, FirstWins, LastWins). -Prefix prepends a string to every dictionary key (e.g. SecretName 'API_KEY' with -Prefix 'MYAPP_' becomes key 'MYAPP_API_KEY'); the underlying InfisicalSecret objects are not mutated. + Aggregates an incoming pipeline of InfisicalSecret objects into a case-insensitive Dictionary keyed by SecretName. By default values are SecureString; pass -AsPlainText to materialize string values. Duplicate keys are handled via the -DuplicateKeyBehavior parameter (Error, FirstWins, LastWins). -Prefix prepends a string to every dictionary key (e.g. SecretName 'API_KEY' with -Prefix 'MYAPP_' becomes key 'MYAPP_API_KEY'); the underlying InfisicalSecret objects are not mutated. A SecretName that already starts with -Prefix (case-insensitive) is left as-is to avoid double-prefixing; pass -ForcePrefix to always prepend. Notes @@ -338,7 +338,7 @@ $ConvertToInfisicalSecretDictionaryResult = ConvertTo-InfisicalSecretDictionary InfisicalSecrets - Buffers an incoming pipeline of InfisicalSecret objects and writes them to a file in the requested format (DotEnv, Json, Yaml, EnvironmentVariables, etc.) or sets them as environment variables on the chosen scope (Process, User, Machine). -Encoding controls text encoding for file outputs. -Prefix prepends a string to every emitted variable name regardless of format. + Buffers an incoming pipeline of InfisicalSecret objects and writes them to a file in the requested format (DotEnv, Json, Yaml, EnvironmentVariables, etc.) or sets them as environment variables on the chosen scope (Process, User, Machine). -Encoding controls text encoding for file outputs. -Prefix prepends a string to every emitted variable name regardless of format; names that already start with -Prefix (case-insensitive) are left as-is to avoid double-prefixing. Pass -ForcePrefix to always prepend. Notes @@ -382,7 +382,7 @@ $ExportInfisicalSecretsResult = Export-InfisicalSecrets @ExportInfisicalSecretsP InfisicalSecret - Loads the file at -Path (which must exist) using the parser matching -Format and returns a case-insensitive Dictionary keyed by SecretName. By default values are SecureString; pass -AsPlainText for a plain string dictionary. -Prefix prepends to every emitted key. -DuplicateKeyBehavior controls collision handling (Error, FirstWins, LastWins). The EnvironmentVariables format is intentionally not supported here; use [Environment]::GetEnvironmentVariable or Get-InfisicalEnvironmentVariable for environment-backed values. + Loads the file at -Path (which must exist) using the parser matching -Format and returns a case-insensitive Dictionary keyed by SecretName. By default values are SecureString; pass -AsPlainText for a plain string dictionary. -Prefix prepends to every emitted key; keys already starting with -Prefix (case-insensitive) are left as-is to avoid double-prefixing, and -ForcePrefix overrides that gate. -DuplicateKeyBehavior controls collision handling (Error, FirstWins, LastWins). The EnvironmentVariables format is intentionally not supported here; use [Environment]::GetEnvironmentVariable or Get-InfisicalEnvironmentVariable for environment-backed values. Notes @@ -1705,7 +1705,7 @@ $WriteInfisicalScepMdmProfileToWmiResult = Write-InfisicalScepMdmProfileToWmi @W InfisicalProcess - Launches the executable specified by -FilePath, captures stdout/stderr, validates the exit code against -AcceptableExitCodeList, and optionally parses output with -ParsingExpression. InfisicalSecret objects supplied via -Secret (pipeline or by name) are decrypted into the ProcessStartInfo.Environment dictionary only, never written to the user or machine scope; -Prefix prepends a string to each injected variable name. -EnvironmentVariables adds additional non-secret values. -ExecutionTimeout, -NoWait, -CreateNoWindow, -WindowStyle, -Priority, -StandardInputObjectList, -SecureArgumentList, -LogOutput, and -ContinueOnError mirror the semantics of the upstream Start-ProcessWithOutput helper. Honors -WhatIf and -Confirm. + Launches the executable specified by -FilePath, captures stdout/stderr, validates the exit code against -AcceptableExitCodeList, and optionally parses output with -ParsingExpression. InfisicalSecret objects supplied via -Secret (pipeline or by name) are decrypted into the ProcessStartInfo.Environment dictionary only, never written to the user or machine scope; -Prefix prepends a string to each injected variable name, skipping names that already start with -Prefix (case-insensitive) unless -ForcePrefix is supplied. -EnvironmentVariables adds additional non-secret values. -ExecutionTimeout, -NoWait, -CreateNoWindow, -WindowStyle, -Priority, -StandardInputObjectList, -SecureArgumentList, -LogOutput, and -ContinueOnError mirror the semantics of the upstream Start-ProcessWithOutput helper. Honors -WhatIf and -Confirm. Notes diff --git a/src/PSInfisicalAPI/Cmdlets/ConvertToInfisicalSecretDictionaryCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/ConvertToInfisicalSecretDictionaryCmdlet.cs index 2009ad3..68768fb 100644 --- a/src/PSInfisicalAPI/Cmdlets/ConvertToInfisicalSecretDictionaryCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/ConvertToInfisicalSecretDictionaryCmdlet.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Management.Automation; using System.Security; +using PSInfisicalAPI.Common; using PSInfisicalAPI.Errors; using PSInfisicalAPI.Models; @@ -24,6 +25,9 @@ namespace PSInfisicalAPI.Cmdlets [Parameter] public string Prefix { get; set; } + [Parameter] + public SwitchParameter ForcePrefix { get; set; } + private readonly List _buffer = new List(); protected override void ProcessRecord() @@ -63,11 +67,10 @@ namespace PSInfisicalAPI.Cmdlets private Dictionary BuildDictionary(Func valueSelector) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); - string prefix = Prefix ?? string.Empty; foreach (InfisicalSecret secret in _buffer) { - string key = string.Concat(prefix, secret.SecretName ?? string.Empty); + string key = InfisicalPrefix.Apply(secret.SecretName ?? string.Empty, Prefix, ForcePrefix.IsPresent); if (dictionary.ContainsKey(key)) { diff --git a/src/PSInfisicalAPI/Cmdlets/ExportInfisicalSecretsCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/ExportInfisicalSecretsCmdlet.cs index 59bb694..21860dd 100644 --- a/src/PSInfisicalAPI/Cmdlets/ExportInfisicalSecretsCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/ExportInfisicalSecretsCmdlet.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Management.Automation; using System.Text; +using PSInfisicalAPI.Common; using PSInfisicalAPI.Errors; using PSInfisicalAPI.Exports; using PSInfisicalAPI.Models; @@ -40,6 +41,9 @@ namespace PSInfisicalAPI.Cmdlets [Parameter] public string Prefix { get; set; } + [Parameter] + public SwitchParameter ForcePrefix { get; set; } + private readonly List _buffer = new List(); protected override void ProcessRecord() @@ -71,7 +75,7 @@ namespace PSInfisicalAPI.Cmdlets InfisicalExportRequest request = new InfisicalExportRequest { - Secrets = ApplyPrefix(_buffer, Prefix), + Secrets = ApplyPrefix(_buffer, Prefix, ForcePrefix.IsPresent), Format = Format, Path = Path, Scope = Scope, @@ -88,7 +92,7 @@ namespace PSInfisicalAPI.Cmdlets } } - private static InfisicalSecret[] ApplyPrefix(List source, string prefix) + private static InfisicalSecret[] ApplyPrefix(List source, string prefix, bool force) { if (string.IsNullOrEmpty(prefix)) { return source.ToArray(); } @@ -104,7 +108,7 @@ namespace PSInfisicalAPI.Cmdlets Environment = original.Environment, Version = original.Version, Type = original.Type, - SecretName = string.Concat(prefix, original.SecretName), + SecretName = InfisicalPrefix.Apply(original.SecretName, prefix, force), SecretValue = original.SecretValue, SecretValueHidden = original.SecretValueHidden, SecretPath = original.SecretPath, diff --git a/src/PSInfisicalAPI/Cmdlets/ImportInfisicalSecretCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/ImportInfisicalSecretCmdlet.cs index 477e99a..87a63ab 100644 --- a/src/PSInfisicalAPI/Cmdlets/ImportInfisicalSecretCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/ImportInfisicalSecretCmdlet.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Management.Automation; using System.Security; +using PSInfisicalAPI.Common; using PSInfisicalAPI.Errors; using PSInfisicalAPI.Imports; using PSInfisicalAPI.Models; @@ -31,6 +32,9 @@ namespace PSInfisicalAPI.Cmdlets [Parameter] public string Prefix { get; set; } + [Parameter] + public SwitchParameter ForcePrefix { get; set; } + protected override void EndProcessing() { try @@ -66,12 +70,11 @@ namespace PSInfisicalAPI.Cmdlets Func valueSelector) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); - string prefix = Prefix ?? string.Empty; foreach (KeyValuePair pair in pairs) { if (pair.Key == null) { continue; } - string key = string.Concat(prefix, pair.Key); + string key = InfisicalPrefix.Apply(pair.Key, Prefix, ForcePrefix.IsPresent); if (dictionary.ContainsKey(key)) { diff --git a/src/PSInfisicalAPI/Cmdlets/StartInfisicalProcessCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/StartInfisicalProcessCmdlet.cs index c129a22..2cd5c18 100644 --- a/src/PSInfisicalAPI/Cmdlets/StartInfisicalProcessCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/StartInfisicalProcessCmdlet.cs @@ -95,6 +95,9 @@ namespace PSInfisicalAPI.Cmdlets [Parameter] public string Prefix { get; set; } + [Parameter] + public SwitchParameter ForcePrefix { get; set; } + private readonly List _secretBuffer = new List(); protected override void ProcessRecord() @@ -135,7 +138,8 @@ namespace PSInfisicalAPI.Cmdlets LogOutput = LogOutput.IsPresent, ContinueOnError = ContinueOnError.IsPresent, Secrets = _secretBuffer.ToArray(), - Prefix = Prefix + Prefix = Prefix, + ForcePrefix = ForcePrefix.IsPresent }; InfisicalProcessResult result = InfisicalProcessRunner.Run(options, Logger); diff --git a/src/PSInfisicalAPI/Common/InfisicalPrefix.cs b/src/PSInfisicalAPI/Common/InfisicalPrefix.cs new file mode 100644 index 0000000..d00e208 --- /dev/null +++ b/src/PSInfisicalAPI/Common/InfisicalPrefix.cs @@ -0,0 +1,15 @@ +using System; + +namespace PSInfisicalAPI.Common +{ + public static class InfisicalPrefix + { + public static string Apply(string original, string prefix, bool force) + { + if (string.IsNullOrEmpty(prefix)) { return original ?? string.Empty; } + if (original == null) { return prefix; } + if (!force && original.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { return original; } + return string.Concat(prefix, original); + } + } +} diff --git a/src/PSInfisicalAPI/Process/InfisicalProcessOptions.cs b/src/PSInfisicalAPI/Process/InfisicalProcessOptions.cs index fc72ead..e081740 100644 --- a/src/PSInfisicalAPI/Process/InfisicalProcessOptions.cs +++ b/src/PSInfisicalAPI/Process/InfisicalProcessOptions.cs @@ -26,5 +26,6 @@ namespace PSInfisicalAPI.Process public bool ContinueOnError { get; set; } public InfisicalSecret[] Secrets { get; set; } public string Prefix { get; set; } + public bool ForcePrefix { get; set; } } } diff --git a/src/PSInfisicalAPI/Process/InfisicalProcessRunnerHelpers.cs b/src/PSInfisicalAPI/Process/InfisicalProcessRunnerHelpers.cs index 239f382..4326f31 100644 --- a/src/PSInfisicalAPI/Process/InfisicalProcessRunnerHelpers.cs +++ b/src/PSInfisicalAPI/Process/InfisicalProcessRunnerHelpers.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; +using PSInfisicalAPI.Common; using PSInfisicalAPI.Logging; using PSInfisicalAPI.Models; using PSInfisicalAPI.Security; @@ -39,7 +40,7 @@ namespace PSInfisicalAPI.Process foreach (InfisicalSecret secret in options.Secrets) { if (secret == null || string.IsNullOrEmpty(secret.SecretName) || secret.SecretValue == null) { continue; } - string name = string.IsNullOrEmpty(options.Prefix) ? secret.SecretName : string.Concat(options.Prefix, secret.SecretName); + string name = InfisicalPrefix.Apply(secret.SecretName, options.Prefix, options.ForcePrefix); SecureStringUtility.UsePlainText(secret.SecretValue, plain => { processEnv[name] = plain;