feat: add Start-InfisicalProcess cmdlet and -Prefix support on Export-InfisicalSecrets #12

Merged
gsadmin merged 4 commits from dev into main 2026-06-06 22:36:23 +00:00
Owner

Summary

Adds a new process-execution cmdlet that natively injects Infisical secrets
into a child process's environment, and adds a -Prefix parameter to
Export-InfisicalSecrets so prefixed naming is supported across every export
format.

Brings the module's exported cmdlet count from 40 to 42.

Changes

Export-InfisicalSecrets-Prefix <string>

  • New optional parameter. When set, every InfisicalSecret is shallow-cloned
    with SecretName = Prefix + SecretName before being handed to the exporter,
    so prefixing applies uniformly to EnvironmentVariables, JSON, DotEnv,
    Yaml, Csv, and PSCredentialList formats without per-exporter changes.
  • No-op when -Prefix is null/empty (early return uses the original array
    reference; pipeline objects are never mutated).

Start-InfisicalProcess (new)

  • Launches a child process with Infisical secrets decrypted directly into
    ProcessStartInfo.Environment. Secret plaintext is never written to user or
    machine environment scope.
  • Pipeline input: InfisicalSecret[] via -Secret (aliases Secrets,
    InputObject). Optional -Prefix mirrors the Export-InfisicalSecrets
    behavior. -EnvironmentVariables supplies additional non-secret variables.
  • Process control: -WorkingDirectory, -ArgumentList,
    -AcceptableExitCodeList (supports *, integer, and hex forms),
    -WindowStyle / -CreateNoWindow parameter sets, -Priority,
    -StandardInputObjectList, -NoWait, -ExecutionTimeout,
    -ExecutionTimeoutInterval, -ParsingExpression (regex over stdout/stderr),
    -SecureArgumentList, -LogOutput, -ContinueOnError, and full
    ShouldProcess (-WhatIf / -Confirm) support.
  • Returns InfisicalProcessResult with ExitCode, ExitCodeAsHex,
    ExitCodeAsInteger, ExitCodeAsDecimal, StandardOutput, StandardError,
    StandardOutputObject, StandardErrorObject, StartTime, ExitTime,
    Duration, DurationFriendly, ProcessId, TimedOut, Succeeded, and
    SecretCount.

PowerShell-safe process I/O

The runner deliberately avoids TPL/async constructs that can deadlock under
the PowerShell host's SynchronizationContext:

  • Stream capture uses event-based OutputDataReceived /
    ErrorDataReceived with BeginOutputReadLine / BeginErrorReadLine. No
    Task<string>, no ReadToEndAsync, no GetAwaiter().GetResult().
  • Wait loop uses the classic do { log; sleep } while (!HasExited) pattern
    with Thread.Sleep(pollInterval), so verbose "has been running for X" and
    "Checking again in Y" messages fire at the configured cadence even when no
    -ExecutionTimeout is supplied (indefinite wait still logs).
  • Exit handling drains the event handlers with a final non-timeout
    WaitForExit() after the polling loop confirms exit, then reads ExitCode
    / ExitTime synchronously.

Friendly TimeSpan formatting

Verbose log messages and the result's DurationFriendly property render
TimeSpan values in the same human-readable form as the legacy
Start-ProcessWithOutput GetTimeSpanMessage scriptblock:

  • 30 seconds
  • 1 minute, and 30 seconds
  • 7 seconds, and 364 milliseconds
  • 1 hour, 2 minutes, 3 seconds, and 45 milliseconds
  • N/A when zero

Singular units drop the trailing s (1 second vs. 2 seconds); zero
components are skipped; the final component is joined with ", and ".

Tests

  • 9 new xUnit cases (InfisicalProcessRunnerHelpersTests) cover
    FormatFriendly singular/plural, multi-unit joining, zero,
    sub-millisecond, and skip-zero-components behavior.
  • Full suite: 227 / 227 passing (was 218).
  • build.ps1 -RunTests clean (build + tests + publish + help stage +
    manifest/import validation green; help XML contains 42 cmdlet entries).

Validated manually

Local smoke harness (scripts/..., gitignored) confirms end-to-end:

  • Env-var injection visible in child's stdout (cmd /c set SMOKE_).
  • Polling loop emits friendly TimeSpan logs each interval against a real
    long-running child (pwsh -c "Start-Sleep -Seconds 7").
  • Forced timeout kills child at threshold (ExecutionTimeout=5s vs.
    Start-Sleep 30); result reports TimedOut=$True, Succeeded=$False,
    ExitCode=-1.
  • No deadlocks observed under any of the above.

Docs

  • README.md — new cmdlet listed and described.
  • docs/DesignSpec.md§16.5 (Export -Prefix) and §16.6
    (Start-InfisicalProcess) updated; InfisicalProcessResult property list
    includes DurationFriendly.
  • Module/PSInfisicalAPI/en-US/PSInfisicalAPI.dll-Help.xmlExport-InfisicalSecrets
    -Prefix example and full Start-InfisicalProcess help entry added.
  • CHANGELOG.md — Unreleased entries for both changes.

Commits in this PR

  • 318db70 feat(export): add -Prefix parameter to Export-InfisicalSecrets
  • d3c7b83 Build artifacts for 318db70480
  • 207e742 feat(process): add Start-InfisicalProcess with event-based capture and friendly TimeSpan logging
  • 15fadd0 Build artifacts for 207e7429e4
## Summary Adds a new process-execution cmdlet that natively injects Infisical secrets into a child process's environment, and adds a `-Prefix` parameter to `Export-InfisicalSecrets` so prefixed naming is supported across every export format. Brings the module's exported cmdlet count from 40 to 42. ## Changes ### `Export-InfisicalSecrets` — `-Prefix <string>` - New optional parameter. When set, every `InfisicalSecret` is shallow-cloned with `SecretName = Prefix + SecretName` before being handed to the exporter, so prefixing applies uniformly to `EnvironmentVariables`, `JSON`, `DotEnv`, `Yaml`, `Csv`, and `PSCredentialList` formats without per-exporter changes. - No-op when `-Prefix` is null/empty (early return uses the original array reference; pipeline objects are never mutated). ### `Start-InfisicalProcess` (new) - Launches a child process with Infisical secrets decrypted directly into `ProcessStartInfo.Environment`. Secret plaintext is never written to user or machine environment scope. - Pipeline input: `InfisicalSecret[]` via `-Secret` (aliases `Secrets`, `InputObject`). Optional `-Prefix` mirrors the `Export-InfisicalSecrets` behavior. `-EnvironmentVariables` supplies additional non-secret variables. - Process control: `-WorkingDirectory`, `-ArgumentList`, `-AcceptableExitCodeList` (supports `*`, integer, and hex forms), `-WindowStyle` / `-CreateNoWindow` parameter sets, `-Priority`, `-StandardInputObjectList`, `-NoWait`, `-ExecutionTimeout`, `-ExecutionTimeoutInterval`, `-ParsingExpression` (regex over stdout/stderr), `-SecureArgumentList`, `-LogOutput`, `-ContinueOnError`, and full `ShouldProcess` (`-WhatIf` / `-Confirm`) support. - Returns `InfisicalProcessResult` with `ExitCode`, `ExitCodeAsHex`, `ExitCodeAsInteger`, `ExitCodeAsDecimal`, `StandardOutput`, `StandardError`, `StandardOutputObject`, `StandardErrorObject`, `StartTime`, `ExitTime`, `Duration`, `DurationFriendly`, `ProcessId`, `TimedOut`, `Succeeded`, and `SecretCount`. ### PowerShell-safe process I/O The runner deliberately avoids TPL/`async` constructs that can deadlock under the PowerShell host's `SynchronizationContext`: - **Stream capture** uses event-based `OutputDataReceived` / `ErrorDataReceived` with `BeginOutputReadLine` / `BeginErrorReadLine`. No `Task<string>`, no `ReadToEndAsync`, no `GetAwaiter().GetResult()`. - **Wait loop** uses the classic `do { log; sleep } while (!HasExited)` pattern with `Thread.Sleep(pollInterval)`, so verbose "has been running for X" and "Checking again in Y" messages fire at the configured cadence even when no `-ExecutionTimeout` is supplied (indefinite wait still logs). - **Exit handling** drains the event handlers with a final non-timeout `WaitForExit()` after the polling loop confirms exit, then reads `ExitCode` / `ExitTime` synchronously. ### Friendly TimeSpan formatting Verbose log messages and the result's `DurationFriendly` property render `TimeSpan` values in the same human-readable form as the legacy `Start-ProcessWithOutput` `GetTimeSpanMessage` scriptblock: - `30 seconds` - `1 minute, and 30 seconds` - `7 seconds, and 364 milliseconds` - `1 hour, 2 minutes, 3 seconds, and 45 milliseconds` - `N/A` when zero Singular units drop the trailing `s` (`1 second` vs. `2 seconds`); zero components are skipped; the final component is joined with `", and "`. ## Tests - 9 new xUnit cases (`InfisicalProcessRunnerHelpersTests`) cover `FormatFriendly` singular/plural, multi-unit joining, zero, sub-millisecond, and skip-zero-components behavior. - Full suite: **227 / 227 passing** (was 218). - `build.ps1 -RunTests` clean (build + tests + publish + help stage + manifest/import validation green; help XML contains 42 cmdlet entries). ## Validated manually Local smoke harness (`scripts/...`, gitignored) confirms end-to-end: - Env-var injection visible in child's stdout (`cmd /c set SMOKE_`). - Polling loop emits friendly TimeSpan logs each interval against a real long-running child (`pwsh -c "Start-Sleep -Seconds 7"`). - Forced timeout kills child at threshold (`ExecutionTimeout=5s` vs. `Start-Sleep 30`); result reports `TimedOut=$True`, `Succeeded=$False`, `ExitCode=-1`. - No deadlocks observed under any of the above. ## Docs - `README.md` — new cmdlet listed and described. - `docs/DesignSpec.md` — `§16.5` (Export `-Prefix`) and `§16.6` (`Start-InfisicalProcess`) updated; `InfisicalProcessResult` property list includes `DurationFriendly`. - `Module/PSInfisicalAPI/en-US/PSInfisicalAPI.dll-Help.xml` — `Export-InfisicalSecrets` `-Prefix` example and full `Start-InfisicalProcess` help entry added. - `CHANGELOG.md` — Unreleased entries for both changes. ## Commits in this PR - `318db70` feat(export): add -Prefix parameter to Export-InfisicalSecrets - `d3c7b83` Build artifacts for 318db7048017 - `207e742` feat(process): add Start-InfisicalProcess with event-based capture and friendly TimeSpan logging - `15fadd0` Build artifacts for 207e7429e448
gsadmin added 4 commits 2026-06-06 22:35:53 +00:00
Adds an optional [string] -Prefix parameter that prepends the supplied
string to every emitted variable name, regardless of -Format
(Json/Yaml/Xml/Env/EnvironmentVariables). When omitted or empty the
exporter buffer is forwarded unchanged (no-op).

Implementation clones each InfisicalSecret with SecretName = Prefix +
SecretName so the caller's pipeline objects are never mutated; the
SecureString and Tags/SecretMetadata array references are shared
(read-only usage downstream).

Also updates the cmdlet help XML description + adds a -Prefix example,
and reflects the new parameter in docs/DesignSpec.md.
Auto-generated by build.ps1 -CommitArtifacts. Build 2026.06.06.2138. Module DLL and manifest embed BuildCommitHash=318db7048017, matching the source commit they were produced from.
- New cmdlet Start-InfisicalProcess: launches a child process with InfisicalSecret
  objects decrypted directly into ProcessStartInfo.Environment (optional -Prefix),
  additional -EnvironmentVariables, stdout/stderr capture, -AcceptableExitCodeList,
  -ParsingExpression regex parsing, -ExecutionTimeout / -ExecutionTimeoutInterval,
  -NoWait, -WindowStyle / -CreateNoWindow parameter sets, -Priority,
  -StandardInputObjectList, -SecureArgumentList, -LogOutput, -ContinueOnError, and
  ShouldProcess support. Secret plaintext is never written to user or machine scope.
- Stream capture uses event-based OutputDataReceived/ErrorDataReceived with
  BeginOutputReadLine/BeginErrorReadLine (no Task / ReadToEndAsync /
  GetAwaiter().GetResult()) to avoid PowerShell SynchronizationContext deadlocks.
- Restored the do { log; sleep } while (!HasExited) polling pattern using
  Thread.Sleep(pollInterval) so verbose "has been running for X" / "Checking again
  in Y" messages fire at the configured cadence even when no -ExecutionTimeout is
  supplied.
- TimeSpan values in verbose logs and on the result now use a friendly format
  ("7 seconds, and 364 milliseconds", "1 minute, and 30 seconds", "N/A" when zero)
  matching the legacy Start-ProcessWithOutput GetTimeSpanMessage scriptblock.
- Added DurationFriendly property to InfisicalProcessResult and a "The command
  execution took X" verbose line at completion.
- build.ps1 CmdletsToExport and Test-ModuleImports expected list contain 42 cmdlets.
- Added 9 xUnit tests covering FormatFriendly singular/plural, multi-unit joining,
  zero, sub-millisecond, and skip-zero-components behavior.
Build artifacts for 207e7429e4
Publish to PowerShell Gallery / build (pull_request) Successful in 24s
Publish to PowerShell Gallery / release (pull_request) Successful in 9s
Publish to PowerShell Gallery / publish (pull_request) Successful in 8s
15fadd01a4
Auto-generated by build.ps1 -CommitArtifacts. Build 2026.06.06.2229. Module DLL and manifest embed BuildCommitHash=207e7429e448, matching the source commit they were produced from.
gsadmin merged commit d0395b54ac into main 2026-06-06 22:36:23 +00:00
Sign in to join this conversation.