Compare commits
11 Commits
2026.6.5.1130
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| f82312433c | |||
| 1aa51b8cbf | |||
| 77cb03ec98 | |||
| d0395b54ac | |||
| 15fadd01a4 | |||
| 207e7429e4 | |||
| d3c7b83da7 | |||
| 318db70480 | |||
| 18f3f3fe2a | |||
| 0fdafeca72 | |||
| 1270c9099c |
+71
-5
@@ -6,35 +6,101 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) loos
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 2026.06.07.0017
|
||||
|
||||
- Build produced from commit 77cb03ec9845.
|
||||
|
||||
## Unreleased (carried forward)
|
||||
|
||||
- Added Organization CRUD cmdlets: `Get-InfisicalOrganization`, `New-InfisicalOrganization`, `Update-InfisicalOrganization`, `Remove-InfisicalOrganization`. `Get` lists every organization the active session can see (List parameter set, default) and returns a single record when `-OrganizationId` is supplied (Single parameter set). `New`/`Update`/`Remove` honor `-WhatIf`/`-Confirm`; `Remove` defaults to High `ConfirmImpact` and supports `-PassThru`. No project context required. Backed by new `InfisicalOrganization` model, DTO, mapper, and client wired into `InfisicalEndpointRegistry` (`ListOrganizations`, `RetrieveOrganization`, `CreateOrganization`, `UpdateOrganization`, `DeleteOrganization`).
|
||||
- Added Sub-Organization CRUD cmdlets: `Get-InfisicalSubOrganization`, `New-InfisicalSubOrganization`, `Update-InfisicalSubOrganization`, `Remove-InfisicalSubOrganization`, targeting the `/api/v1/sub-organizations` Beta endpoints. `Get` lists by default and accepts optional `-Limit`, `-Offset`, `-Search`, `-OrderBy`, `-OrderDirection`, and `-IsAccessible` query parameters; supplying `-SubOrganizationId` returns a single record. `New`/`Update`/`Remove` honor `-WhatIf`/`-Confirm`; `Remove` defaults to High `ConfirmImpact` and supports `-PassThru`. No project context required. Backed by new `InfisicalSubOrganization` model, DTO, mapper, and client wired into `InfisicalEndpointRegistry` (`ListSubOrganizations`, `RetrieveSubOrganization`, `CreateSubOrganization`, `UpdateSubOrganization`, `DeleteSubOrganization`).
|
||||
- Added `Get-InfisicalSANList` cmdlet: emits a deduplicated SAN candidate set containing the local device name, the device name suffixed with each non-empty DNS suffix found across operational adapters and the system primary domain, every IPv4 unicast address falling within RFC 1918 (10/8, 172.16/12, 192.168/16) or CGNAT (100.64/10), and the IPv4/IPv6 loopback addresses (127.0.0.1, ::1). Intended to feed `Request-InfisicalCertificate -DnsName` directly.
|
||||
- `Get-InfisicalSANList`: added optional `-InclusionExpression` and `-ExclusionExpression` case-insensitive regex filters. Applied in fetch -> include -> exclude -> output order after the deduplicated set is built; both default to unset (no filtering).
|
||||
- `Get-InfisicalSANList`: output is a single strongly-typed `System.String[]` array emitted non-enumerated (`OutputType(string[])`), so variable assignment yields `string[]` rather than `object[]`. This lets `[System.Collections.Generic.List[string]]::AddRange()` consume the result directly and lets the array bind straight to `string[]` parameters such as `Request-InfisicalCertificate -DnsName`.
|
||||
- `build.ps1` `CmdletsToExport` and `Test-ModuleImports` expected list now contain 51 cmdlets. `docs/DesignSpec.md` updated with `§16.7` (Organizations) and `§16.8` (Sub-Organizations); full MAML help added for all 9 new cmdlets in `Module/PSInfisicalAPI/en-US/PSInfisicalAPI.dll-Help.xml`.
|
||||
|
||||
## 2026.06.06.2229
|
||||
|
||||
- Build produced from commit 207e7429e448.
|
||||
|
||||
## Unreleased (carried forward)
|
||||
|
||||
- `Start-InfisicalProcess`: switched stdout/stderr capture to event-based `OutputDataReceived`/`ErrorDataReceived` with `BeginOutputReadLine`/`BeginErrorReadLine` (removed `Task`/`ReadToEndAsync`/`GetAwaiter().GetResult()` to eliminate PowerShell `SynchronizationContext` deadlock risk). Restored the original `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.
|
||||
- `Start-InfisicalProcess`: 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.
|
||||
|
||||
## 2026.06.06.2227
|
||||
|
||||
- Build produced from commit d3c7b83da717.
|
||||
|
||||
## Unreleased (carried forward)
|
||||
|
||||
## 2026.06.06.2221
|
||||
|
||||
- Build produced from commit d3c7b83da717.
|
||||
|
||||
## Unreleased (carried forward)
|
||||
|
||||
## 2026.06.06.2207
|
||||
|
||||
- Build produced from commit d3c7b83da717.
|
||||
|
||||
## Unreleased (carried forward)
|
||||
|
||||
## 2026.06.06.2206
|
||||
|
||||
- Build produced from commit d3c7b83da717.
|
||||
|
||||
## Unreleased (carried forward)
|
||||
|
||||
## 2026.06.06.2155
|
||||
|
||||
- Build produced from commit d3c7b83da717.
|
||||
|
||||
## Unreleased (carried forward)
|
||||
|
||||
- Added `Start-InfisicalProcess` cmdlet: launches a child process with `InfisicalSecret` objects (pipeline or `-Secret`) decrypted directly into `ProcessStartInfo.Environment`, with optional `-Prefix`, additional `-EnvironmentVariables`, stdout/stderr capture, `-AcceptableExitCodeList` validation, `-ParsingExpression` regex parsing, `-ExecutionTimeout`/`-ExecutionTimeoutInterval` polling, `-NoWait`, `-WindowStyle`/`-CreateNoWindow` parameter sets, `-Priority`, `-StandardInputObjectList`, `-SecureArgumentList`, `-LogOutput`, `-ContinueOnError`, and `ShouldProcess` support. Secret plaintext is never written to user or machine scope. `build.ps1` `CmdletsToExport` and `Test-ModuleImports` expected list now contain 42 cmdlets.
|
||||
|
||||
## 2026.06.06.2138
|
||||
|
||||
- Build produced from commit 318db7048017.
|
||||
|
||||
## Unreleased (carried forward)
|
||||
|
||||
## 2026.06.05.2040
|
||||
|
||||
- Build produced from commit 1270c9099cae.
|
||||
|
||||
## Unreleased (carried forward)
|
||||
|
||||
## 2026.06.05.0240
|
||||
|
||||
- Build produced from commit b438abf18f18.
|
||||
|
||||
## Unreleased (carried forward)
|
||||
## Unreleased (carried forward)
|
||||
|
||||
## 2026.06.05.0215
|
||||
|
||||
- Build produced from commit 82f99ea7d4a4.
|
||||
|
||||
## Unreleased (carried forward)
|
||||
## Unreleased (carried forward)
|
||||
|
||||
## 2026.06.05.0205
|
||||
|
||||
- Build produced from commit 86968c18cb15.
|
||||
|
||||
## Unreleased (carried forward)
|
||||
## Unreleased (carried forward)
|
||||
|
||||
## 2026.06.05.0117
|
||||
|
||||
- Build produced from commit cffda99591c9.
|
||||
|
||||
## Unreleased (carried forward)
|
||||
## Unreleased (carried forward)
|
||||
|
||||
## 2026.06.05.0015
|
||||
|
||||
- Build produced from commit fb27ab8a8503.
|
||||
|
||||
## Unreleased (carried forward)
|
||||
## Unreleased (carried forward)
|
||||
|
||||
- Fixed `ParameterNameConflictsWithAlias` registration error on `Get-InfisicalCertificateApplication`, `Get-InfisicalCertificateApplicationEnrollment`, and `New-InfisicalScepDynamicChallenge`. The cmdlets each declared an `[Alias]` entry that matched the parameter's own name, which PowerShell rejects at bind time and made the cmdlets unusable.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@{
|
||||
RootModule = 'PSInfisicalAPI.psm1'
|
||||
ModuleVersion = '2026.06.05.0240'
|
||||
ModuleVersion = '2026.06.07.0017'
|
||||
GUID = 'b8a2f3d4-7c51-4d2f-9e6a-1f0c8b3d4e51'
|
||||
Author = 'Grace Solutions'
|
||||
CompanyName = 'Grace Solutions'
|
||||
@@ -35,6 +35,14 @@
|
||||
'New-InfisicalTag',
|
||||
'Update-InfisicalTag',
|
||||
'Remove-InfisicalTag',
|
||||
'Get-InfisicalOrganization',
|
||||
'New-InfisicalOrganization',
|
||||
'Update-InfisicalOrganization',
|
||||
'Remove-InfisicalOrganization',
|
||||
'Get-InfisicalSubOrganization',
|
||||
'New-InfisicalSubOrganization',
|
||||
'Update-InfisicalSubOrganization',
|
||||
'Remove-InfisicalSubOrganization',
|
||||
'Get-InfisicalCertificateAuthority',
|
||||
'Get-InfisicalPkiSubscriber',
|
||||
'Get-InfisicalCertificateProfile',
|
||||
@@ -50,7 +58,9 @@
|
||||
'New-InfisicalScepDynamicChallenge',
|
||||
'Get-InfisicalScepMdmProfile',
|
||||
'Export-InfisicalScepMdmProfile',
|
||||
'Write-InfisicalScepMdmProfileToWmi'
|
||||
'Write-InfisicalScepMdmProfileToWmi',
|
||||
'Start-InfisicalProcess',
|
||||
'Get-InfisicalSANList'
|
||||
)
|
||||
AliasesToExport = @()
|
||||
VariablesToExport = @()
|
||||
@@ -62,7 +72,7 @@
|
||||
LicenseUri = 'https://www.gnu.org/licenses/agpl-3.0.html'
|
||||
ProjectUri = 'https://prod.git.gracesolution.info/gsadmin/PSInfisicalAPI'
|
||||
ReleaseNotes = 'See CHANGELOG.md in the project repository for release history.'
|
||||
CommitHash = 'b438abf18f18'
|
||||
CommitHash = '77cb03ec9845'
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -333,7 +333,7 @@ $ConvertToInfisicalSecretDictionaryResult = ConvertTo-InfisicalSecretDictionary
|
||||
<command:noun>InfisicalSecrets</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>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.</maml:para>
|
||||
<maml:para>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.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
@@ -361,6 +361,11 @@ $ExportInfisicalSecretsParameters.Verbose = $True
|
||||
$ExportInfisicalSecretsResult = Export-InfisicalSecrets @ExportInfisicalSecretsParameters</dev:code>
|
||||
<dev:remarks><maml:para>Projects the recursive secret result into Process-scope environment variables for the current PowerShell session.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 3</maml:title>
|
||||
<dev:code>Get-InfisicalSecret -ProjectId $ProjectId -Environment 'dev' | Export-InfisicalSecrets -Format EnvironmentVariables -Scope Process -Prefix 'MYAPP_'</dev:code>
|
||||
<dev:remarks><maml:para>Imports secrets into the process environment with every variable name prefixed by 'MYAPP_' (e.g. API_KEY becomes MYAPP_API_KEY).</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
@@ -1649,4 +1654,369 @@ $WriteInfisicalScepMdmProfileToWmiResult = Write-InfisicalScepMdmProfileToWmi @W
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||
<command:details>
|
||||
<command:name>Start-InfisicalProcess</command:name>
|
||||
<maml:description><maml:para>Starts a child process with Infisical secrets injected directly into its environment block.</maml:para></maml:description>
|
||||
<command:verb>Start</command:verb>
|
||||
<command:noun>InfisicalProcess</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>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.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
<maml:alert>
|
||||
<maml:para>Secret values exist as plain strings only within the child process environment block; they are never persisted to the calling shell, the user scope, or the machine scope. Use -SecureArgumentList to mask sensitive command-line arguments in verbose output.</maml:para>
|
||||
</maml:alert>
|
||||
</maml:alertSet>
|
||||
<command:examples>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 1</maml:title>
|
||||
<dev:code>Get-InfisicalSecret -SecretPath '/build' | Start-InfisicalProcess -FilePath 'dotnet.exe' -ArgumentList @('publish','-c','Release') -AcceptableExitCodeList @('0') -CreateNoWindow</dev:code>
|
||||
<dev:remarks><maml:para>Decrypts every secret at /build, exposes each one as a process environment variable, and runs dotnet publish with no visible window.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 2</maml:title>
|
||||
<dev:code>$Secrets = Get-InfisicalSecret -SecretPath '/runtime'
|
||||
Start-InfisicalProcess -FilePath 'node.exe' -ArgumentList @('app.js') -Secret $Secrets -Prefix 'APP_' -ExecutionTimeout ([TimeSpan]::FromMinutes(5)) -LogOutput</dev:code>
|
||||
<dev:remarks><maml:para>Injects the /runtime secrets as APP_-prefixed environment variables, runs node app.js, and forcibly terminates the process after five minutes if it has not exited.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 3</maml:title>
|
||||
<dev:code>$StartInfisicalProcessParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
$StartInfisicalProcessParameters.FilePath = 'pwsh.exe'
|
||||
$StartInfisicalProcessParameters.ArgumentList = @('-NoProfile','-Command','Write-Host $env:DEPLOY_TOKEN.Length')
|
||||
$StartInfisicalProcessParameters.Secret = Get-InfisicalSecret -SecretPath '/deploy'
|
||||
$StartInfisicalProcessParameters.Prefix = 'DEPLOY_'
|
||||
$StartInfisicalProcessParameters.AcceptableExitCodeList = @('0')
|
||||
$StartInfisicalProcessParameters.CreateNoWindow = $True
|
||||
$StartInfisicalProcessParameters.SecureArgumentList = $True
|
||||
$StartInfisicalProcessParameters.LogOutput = $True
|
||||
$StartInfisicalProcessParameters.Verbose = $True
|
||||
|
||||
$StartInfisicalProcessResult = Start-InfisicalProcess @StartInfisicalProcessParameters</dev:code>
|
||||
<dev:remarks><maml:para>Splatted invocation that runs pwsh with DEPLOY_-prefixed secrets in scope, masks the command line in verbose output, and echoes both stdout and stderr to the verbose stream after exit.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||
<command:details>
|
||||
<command:name>Get-InfisicalOrganization</command:name>
|
||||
<maml:description><maml:para>Lists or retrieves Infisical organizations accessible to the current identity.</maml:para></maml:description>
|
||||
<command:verb>Get</command:verb>
|
||||
<command:noun>InfisicalOrganization</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>Default (List parameter set) returns every organization the active session can see; visibility is governed by Infisical's role assignments. When -OrganizationId is supplied (Single parameter set) the cmdlet returns one organization. Does not require a project context.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
<maml:alert>
|
||||
<maml:para>The List-mode result is an array of InfisicalOrganization objects; pipe into Where-Object or Select-Object to filter by Slug, Name, or Id. The cmdlet accepts pipeline input by property name on -OrganizationId.</maml:para>
|
||||
</maml:alert>
|
||||
</maml:alertSet>
|
||||
<command:examples>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 1</maml:title>
|
||||
<dev:code>Get-InfisicalOrganization</dev:code>
|
||||
<dev:remarks><maml:para>Lists every organization the current session can see.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 2</maml:title>
|
||||
<dev:code>Get-InfisicalOrganization -OrganizationId $OrganizationId</dev:code>
|
||||
<dev:remarks><maml:para>Retrieves the canonical record for a single organization by id.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||
<command:details>
|
||||
<command:name>New-InfisicalOrganization</command:name>
|
||||
<maml:description><maml:para>Creates a new Infisical organization.</maml:para></maml:description>
|
||||
<command:verb>New</command:verb>
|
||||
<command:noun>InfisicalOrganization</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>Creates a new organization with the supplied name and optional slug. Honors -WhatIf and -Confirm. Requires server-side permission to create organizations.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
<maml:alert>
|
||||
<maml:para>Slug must be unique server-side; if omitted, the server derives one from the name.</maml:para>
|
||||
</maml:alert>
|
||||
</maml:alertSet>
|
||||
<command:examples>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 1</maml:title>
|
||||
<dev:code>New-InfisicalOrganization -Name 'Acme Corporation'</dev:code>
|
||||
<dev:remarks><maml:para>Creates a new organization named 'Acme Corporation' with a server-derived slug.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 2</maml:title>
|
||||
<dev:code>$NewInfisicalOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
$NewInfisicalOrganizationParameters.Name = 'Acme Corporation'
|
||||
$NewInfisicalOrganizationParameters.Slug = 'acme-corp'
|
||||
$NewInfisicalOrganizationParameters.Verbose = $True
|
||||
|
||||
$NewInfisicalOrganizationResult = New-InfisicalOrganization @NewInfisicalOrganizationParameters</dev:code>
|
||||
<dev:remarks><maml:para>Creates an organization with an explicit slug.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||
<command:details>
|
||||
<command:name>Update-InfisicalOrganization</command:name>
|
||||
<maml:description><maml:para>Updates mutable attributes on an existing Infisical organization.</maml:para></maml:description>
|
||||
<command:verb>Update</command:verb>
|
||||
<command:noun>InfisicalOrganization</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>Updates the name or slug of an organization. -OrganizationId is required. Only bound parameters are transmitted. Honors -WhatIf and -Confirm.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
<maml:alert>
|
||||
<maml:para>Renaming or re-slugging an organization may affect billing exports and identity URLs; coordinate with downstream consumers.</maml:para>
|
||||
</maml:alert>
|
||||
</maml:alertSet>
|
||||
<command:examples>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 1</maml:title>
|
||||
<dev:code>Update-InfisicalOrganization -OrganizationId $OrganizationId -Name 'Acme Corp.'</dev:code>
|
||||
<dev:remarks><maml:para>Renames the supplied organization.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 2</maml:title>
|
||||
<dev:code>$UpdateInfisicalOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
$UpdateInfisicalOrganizationParameters.OrganizationId = $OrganizationId
|
||||
$UpdateInfisicalOrganizationParameters.Name = 'Acme Corp.'
|
||||
$UpdateInfisicalOrganizationParameters.Slug = 'acme-corp'
|
||||
$UpdateInfisicalOrganizationParameters.Verbose = $True
|
||||
|
||||
$UpdateInfisicalOrganizationResult = Update-InfisicalOrganization @UpdateInfisicalOrganizationParameters</dev:code>
|
||||
<dev:remarks><maml:para>Renames the organization and updates its slug.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||
<command:details>
|
||||
<command:name>Remove-InfisicalOrganization</command:name>
|
||||
<maml:description><maml:para>Deletes an Infisical organization.</maml:para></maml:description>
|
||||
<command:verb>Remove</command:verb>
|
||||
<command:noun>InfisicalOrganization</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>Deletes an organization by id. -OrganizationId is required. High ConfirmImpact prompts unless -Confirm:$False is supplied. -PassThru emits the removed organization id.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
<maml:alert>
|
||||
<maml:para>This is irreversible and removes all projects, sub-organizations, secrets, and identities owned by the organization. Honors -WhatIf and -Confirm.</maml:para>
|
||||
</maml:alert>
|
||||
</maml:alertSet>
|
||||
<command:examples>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 1</maml:title>
|
||||
<dev:code>Remove-InfisicalOrganization -OrganizationId $OrganizationId -Confirm:$False</dev:code>
|
||||
<dev:remarks><maml:para>Deletes the supplied organization without prompting.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 2</maml:title>
|
||||
<dev:code>$RemoveInfisicalOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
$RemoveInfisicalOrganizationParameters.OrganizationId = $OrganizationId
|
||||
$RemoveInfisicalOrganizationParameters.PassThru = $True
|
||||
$RemoveInfisicalOrganizationParameters.Confirm = $False
|
||||
$RemoveInfisicalOrganizationParameters.Verbose = $True
|
||||
|
||||
$RemoveInfisicalOrganizationResult = Remove-InfisicalOrganization @RemoveInfisicalOrganizationParameters</dev:code>
|
||||
<dev:remarks><maml:para>Removes the organization without confirmation and emits the removed organization id for logging.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||
<command:details>
|
||||
<command:name>Get-InfisicalSubOrganization</command:name>
|
||||
<maml:description><maml:para>Lists or retrieves Infisical sub-organizations accessible to the current identity.</maml:para></maml:description>
|
||||
<command:verb>Get</command:verb>
|
||||
<command:noun>InfisicalSubOrganization</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>Default (List parameter set) returns every sub-organization the active session can see. Optional -Limit, -Offset, -Search, -OrderBy, -OrderDirection, and -IsAccessible are forwarded to the server as query parameters. When -SubOrganizationId is supplied (Single parameter set) the cmdlet returns one sub-organization. Does not require a project context.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
<maml:alert>
|
||||
<maml:para>Sub-organizations are a beta Infisical feature. The List result is an array of InfisicalSubOrganization objects; pipe into Where-Object or Select-Object to filter further. The cmdlet accepts pipeline input by property name on -SubOrganizationId.</maml:para>
|
||||
</maml:alert>
|
||||
</maml:alertSet>
|
||||
<command:examples>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 1</maml:title>
|
||||
<dev:code>Get-InfisicalSubOrganization</dev:code>
|
||||
<dev:remarks><maml:para>Lists every sub-organization the current session can see.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 2</maml:title>
|
||||
<dev:code>Get-InfisicalSubOrganization -SubOrganizationId $SubOrganizationId</dev:code>
|
||||
<dev:remarks><maml:para>Retrieves the canonical record for a single sub-organization by id.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 3</maml:title>
|
||||
<dev:code>Get-InfisicalSubOrganization -Search 'platform' -OrderBy 'name' -OrderDirection 'asc' -Limit 25 -IsAccessible</dev:code>
|
||||
<dev:remarks><maml:para>Lists up to 25 sub-organizations matching 'platform', sorted ascending by name, restricted to those the current identity has access to.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||
<command:details>
|
||||
<command:name>New-InfisicalSubOrganization</command:name>
|
||||
<maml:description><maml:para>Creates a new Infisical sub-organization.</maml:para></maml:description>
|
||||
<command:verb>New</command:verb>
|
||||
<command:noun>InfisicalSubOrganization</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>Creates a sub-organization with the supplied name and slug. Both are required by the server. Honors -WhatIf and -Confirm.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
<maml:alert>
|
||||
<maml:para>Slug must be unique within the parent organization. Sub-organizations are a beta Infisical feature.</maml:para>
|
||||
</maml:alert>
|
||||
</maml:alertSet>
|
||||
<command:examples>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 1</maml:title>
|
||||
<dev:code>New-InfisicalSubOrganization -Name 'Platform Engineering' -Slug 'platform-eng'</dev:code>
|
||||
<dev:remarks><maml:para>Creates a new sub-organization named 'Platform Engineering' with slug 'platform-eng'.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 2</maml:title>
|
||||
<dev:code>$NewInfisicalSubOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
$NewInfisicalSubOrganizationParameters.Name = 'Platform Engineering'
|
||||
$NewInfisicalSubOrganizationParameters.Slug = 'platform-eng'
|
||||
$NewInfisicalSubOrganizationParameters.Verbose = $True
|
||||
|
||||
$NewInfisicalSubOrganizationResult = New-InfisicalSubOrganization @NewInfisicalSubOrganizationParameters</dev:code>
|
||||
<dev:remarks><maml:para>Splatted invocation that creates a sub-organization and logs the request via the verbose stream.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||
<command:details>
|
||||
<command:name>Update-InfisicalSubOrganization</command:name>
|
||||
<maml:description><maml:para>Updates mutable attributes on an existing Infisical sub-organization.</maml:para></maml:description>
|
||||
<command:verb>Update</command:verb>
|
||||
<command:noun>InfisicalSubOrganization</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>Updates the name or slug of a sub-organization. -SubOrganizationId is required. Only bound parameters are transmitted. Honors -WhatIf and -Confirm.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
<maml:alert>
|
||||
<maml:para>Sub-organizations are a beta Infisical feature; coordinate slug changes with downstream consumers that pin the slug in scripts or configuration files.</maml:para>
|
||||
</maml:alert>
|
||||
</maml:alertSet>
|
||||
<command:examples>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 1</maml:title>
|
||||
<dev:code>Update-InfisicalSubOrganization -SubOrganizationId $SubOrganizationId -Name 'Platform (v2)'</dev:code>
|
||||
<dev:remarks><maml:para>Renames the supplied sub-organization.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 2</maml:title>
|
||||
<dev:code>$UpdateInfisicalSubOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
$UpdateInfisicalSubOrganizationParameters.SubOrganizationId = $SubOrganizationId
|
||||
$UpdateInfisicalSubOrganizationParameters.Name = 'Platform (v2)'
|
||||
$UpdateInfisicalSubOrganizationParameters.Slug = 'platform-v2'
|
||||
$UpdateInfisicalSubOrganizationParameters.Verbose = $True
|
||||
|
||||
$UpdateInfisicalSubOrganizationResult = Update-InfisicalSubOrganization @UpdateInfisicalSubOrganizationParameters</dev:code>
|
||||
<dev:remarks><maml:para>Renames the sub-organization and updates its slug in a single call.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||
<command:details>
|
||||
<command:name>Remove-InfisicalSubOrganization</command:name>
|
||||
<maml:description><maml:para>Deletes an Infisical sub-organization.</maml:para></maml:description>
|
||||
<command:verb>Remove</command:verb>
|
||||
<command:noun>InfisicalSubOrganization</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>Deletes a sub-organization by id. -SubOrganizationId is required. High ConfirmImpact prompts unless -Confirm:$False is supplied. -PassThru emits the removed sub-organization id.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
<maml:alert>
|
||||
<maml:para>This is destructive and removes all projects, secrets, and identities scoped to the sub-organization. Honors -WhatIf and -Confirm.</maml:para>
|
||||
</maml:alert>
|
||||
</maml:alertSet>
|
||||
<command:examples>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 1</maml:title>
|
||||
<dev:code>Remove-InfisicalSubOrganization -SubOrganizationId $SubOrganizationId -Confirm:$False</dev:code>
|
||||
<dev:remarks><maml:para>Deletes the supplied sub-organization without prompting.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 2</maml:title>
|
||||
<dev:code>$RemoveInfisicalSubOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
$RemoveInfisicalSubOrganizationParameters.SubOrganizationId = $SubOrganizationId
|
||||
$RemoveInfisicalSubOrganizationParameters.PassThru = $True
|
||||
$RemoveInfisicalSubOrganizationParameters.Confirm = $False
|
||||
$RemoveInfisicalSubOrganizationParameters.Verbose = $True
|
||||
|
||||
$RemoveInfisicalSubOrganizationResult = Remove-InfisicalSubOrganization @RemoveInfisicalSubOrganizationParameters</dev:code>
|
||||
<dev:remarks><maml:para>Removes the sub-organization without confirmation and emits the removed id for logging.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||
<command:details>
|
||||
<command:name>Get-InfisicalSANList</command:name>
|
||||
<maml:description><maml:para>Builds a deduplicated list of Subject Alternative Name candidates for the local device.</maml:para></maml:description>
|
||||
<command:verb>Get</command:verb>
|
||||
<command:noun>InfisicalSANList</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>Returns, in order: the local device name; the device name suffixed with each non-empty DNS suffix found on any operational (non-loopback) network adapter and the system primary domain; every IPv4 unicast address whose first octets fall within RFC 1918 (10/8, 172.16/12, 192.168/16) or CGNAT (100.64/10); and the IPv4 and IPv6 loopback addresses (127.0.0.1, ::1). Optional -InclusionExpression and -ExclusionExpression regex filters are applied in that order after collection, before output. Suitable as a one-shot SAN provider for Request-InfisicalCertificate -DnsName.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
<maml:alert>
|
||||
<maml:para>Output is a single strongly-typed System.String[] array (emitted non-enumerated) so it round-trips into [System.Collections.Generic.List[string]]::AddRange() and binds directly to string[] parameters such as Request-InfisicalCertificate -DnsName. The device name comes first so it can be reused as a CommonName. Routable public IPv4 addresses, link-local addresses, and IPv6 unicast addresses other than loopback are intentionally excluded. -InclusionExpression and -ExclusionExpression are case-insensitive .NET regular expressions; inclusion is applied first, then exclusion.</maml:para>
|
||||
</maml:alert>
|
||||
</maml:alertSet>
|
||||
<command:examples>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 1</maml:title>
|
||||
<dev:code>Get-InfisicalSANList</dev:code>
|
||||
<dev:remarks><maml:para>Returns the SAN candidate list for the current device.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 2</maml:title>
|
||||
<dev:code>$Sans = Get-InfisicalSANList
|
||||
Request-InfisicalCertificate -ProjectId $ProjectId -CertificateAuthorityId $CaId -CommonName $Sans[0] -DnsName $Sans -Ttl '90d'</dev:code>
|
||||
<dev:remarks><maml:para>Captures the SAN list, then uses the device name as the CommonName and the full list as DnsName when requesting a certificate.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 3</maml:title>
|
||||
<dev:code>$GetInfisicalSANListParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
$GetInfisicalSANListParameters.InclusionExpression = '\.gracesolution\.prv$|^10\.|^172\.'
|
||||
$GetInfisicalSANListParameters.ExclusionExpression = '^127\.|^::1$'
|
||||
$Sans = Get-InfisicalSANList @GetInfisicalSANListParameters</dev:code>
|
||||
<dev:remarks><maml:para>Keeps only entries ending in the corporate DNS suffix or sitting in the 10/8 or 172/12 ranges, then drops loopback. Filters are case-insensitive and applied in fetch -> include -> exclude -> output order.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
</helpItems>
|
||||
|
||||
@@ -333,7 +333,7 @@ $ConvertToInfisicalSecretDictionaryResult = ConvertTo-InfisicalSecretDictionary
|
||||
<command:noun>InfisicalSecrets</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>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.</maml:para>
|
||||
<maml:para>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.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
@@ -361,6 +361,11 @@ $ExportInfisicalSecretsParameters.Verbose = $True
|
||||
$ExportInfisicalSecretsResult = Export-InfisicalSecrets @ExportInfisicalSecretsParameters</dev:code>
|
||||
<dev:remarks><maml:para>Projects the recursive secret result into Process-scope environment variables for the current PowerShell session.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 3</maml:title>
|
||||
<dev:code>Get-InfisicalSecret -ProjectId $ProjectId -Environment 'dev' | Export-InfisicalSecrets -Format EnvironmentVariables -Scope Process -Prefix 'MYAPP_'</dev:code>
|
||||
<dev:remarks><maml:para>Imports secrets into the process environment with every variable name prefixed by 'MYAPP_' (e.g. API_KEY becomes MYAPP_API_KEY).</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
@@ -1649,4 +1654,369 @@ $WriteInfisicalScepMdmProfileToWmiResult = Write-InfisicalScepMdmProfileToWmi @W
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||
<command:details>
|
||||
<command:name>Start-InfisicalProcess</command:name>
|
||||
<maml:description><maml:para>Starts a child process with Infisical secrets injected directly into its environment block.</maml:para></maml:description>
|
||||
<command:verb>Start</command:verb>
|
||||
<command:noun>InfisicalProcess</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>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.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
<maml:alert>
|
||||
<maml:para>Secret values exist as plain strings only within the child process environment block; they are never persisted to the calling shell, the user scope, or the machine scope. Use -SecureArgumentList to mask sensitive command-line arguments in verbose output.</maml:para>
|
||||
</maml:alert>
|
||||
</maml:alertSet>
|
||||
<command:examples>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 1</maml:title>
|
||||
<dev:code>Get-InfisicalSecret -SecretPath '/build' | Start-InfisicalProcess -FilePath 'dotnet.exe' -ArgumentList @('publish','-c','Release') -AcceptableExitCodeList @('0') -CreateNoWindow</dev:code>
|
||||
<dev:remarks><maml:para>Decrypts every secret at /build, exposes each one as a process environment variable, and runs dotnet publish with no visible window.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 2</maml:title>
|
||||
<dev:code>$Secrets = Get-InfisicalSecret -SecretPath '/runtime'
|
||||
Start-InfisicalProcess -FilePath 'node.exe' -ArgumentList @('app.js') -Secret $Secrets -Prefix 'APP_' -ExecutionTimeout ([TimeSpan]::FromMinutes(5)) -LogOutput</dev:code>
|
||||
<dev:remarks><maml:para>Injects the /runtime secrets as APP_-prefixed environment variables, runs node app.js, and forcibly terminates the process after five minutes if it has not exited.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 3</maml:title>
|
||||
<dev:code>$StartInfisicalProcessParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
$StartInfisicalProcessParameters.FilePath = 'pwsh.exe'
|
||||
$StartInfisicalProcessParameters.ArgumentList = @('-NoProfile','-Command','Write-Host $env:DEPLOY_TOKEN.Length')
|
||||
$StartInfisicalProcessParameters.Secret = Get-InfisicalSecret -SecretPath '/deploy'
|
||||
$StartInfisicalProcessParameters.Prefix = 'DEPLOY_'
|
||||
$StartInfisicalProcessParameters.AcceptableExitCodeList = @('0')
|
||||
$StartInfisicalProcessParameters.CreateNoWindow = $True
|
||||
$StartInfisicalProcessParameters.SecureArgumentList = $True
|
||||
$StartInfisicalProcessParameters.LogOutput = $True
|
||||
$StartInfisicalProcessParameters.Verbose = $True
|
||||
|
||||
$StartInfisicalProcessResult = Start-InfisicalProcess @StartInfisicalProcessParameters</dev:code>
|
||||
<dev:remarks><maml:para>Splatted invocation that runs pwsh with DEPLOY_-prefixed secrets in scope, masks the command line in verbose output, and echoes both stdout and stderr to the verbose stream after exit.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||
<command:details>
|
||||
<command:name>Get-InfisicalOrganization</command:name>
|
||||
<maml:description><maml:para>Lists or retrieves Infisical organizations accessible to the current identity.</maml:para></maml:description>
|
||||
<command:verb>Get</command:verb>
|
||||
<command:noun>InfisicalOrganization</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>Default (List parameter set) returns every organization the active session can see; visibility is governed by Infisical's role assignments. When -OrganizationId is supplied (Single parameter set) the cmdlet returns one organization. Does not require a project context.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
<maml:alert>
|
||||
<maml:para>The List-mode result is an array of InfisicalOrganization objects; pipe into Where-Object or Select-Object to filter by Slug, Name, or Id. The cmdlet accepts pipeline input by property name on -OrganizationId.</maml:para>
|
||||
</maml:alert>
|
||||
</maml:alertSet>
|
||||
<command:examples>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 1</maml:title>
|
||||
<dev:code>Get-InfisicalOrganization</dev:code>
|
||||
<dev:remarks><maml:para>Lists every organization the current session can see.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 2</maml:title>
|
||||
<dev:code>Get-InfisicalOrganization -OrganizationId $OrganizationId</dev:code>
|
||||
<dev:remarks><maml:para>Retrieves the canonical record for a single organization by id.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||
<command:details>
|
||||
<command:name>New-InfisicalOrganization</command:name>
|
||||
<maml:description><maml:para>Creates a new Infisical organization.</maml:para></maml:description>
|
||||
<command:verb>New</command:verb>
|
||||
<command:noun>InfisicalOrganization</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>Creates a new organization with the supplied name and optional slug. Honors -WhatIf and -Confirm. Requires server-side permission to create organizations.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
<maml:alert>
|
||||
<maml:para>Slug must be unique server-side; if omitted, the server derives one from the name.</maml:para>
|
||||
</maml:alert>
|
||||
</maml:alertSet>
|
||||
<command:examples>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 1</maml:title>
|
||||
<dev:code>New-InfisicalOrganization -Name 'Acme Corporation'</dev:code>
|
||||
<dev:remarks><maml:para>Creates a new organization named 'Acme Corporation' with a server-derived slug.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 2</maml:title>
|
||||
<dev:code>$NewInfisicalOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
$NewInfisicalOrganizationParameters.Name = 'Acme Corporation'
|
||||
$NewInfisicalOrganizationParameters.Slug = 'acme-corp'
|
||||
$NewInfisicalOrganizationParameters.Verbose = $True
|
||||
|
||||
$NewInfisicalOrganizationResult = New-InfisicalOrganization @NewInfisicalOrganizationParameters</dev:code>
|
||||
<dev:remarks><maml:para>Creates an organization with an explicit slug.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||
<command:details>
|
||||
<command:name>Update-InfisicalOrganization</command:name>
|
||||
<maml:description><maml:para>Updates mutable attributes on an existing Infisical organization.</maml:para></maml:description>
|
||||
<command:verb>Update</command:verb>
|
||||
<command:noun>InfisicalOrganization</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>Updates the name or slug of an organization. -OrganizationId is required. Only bound parameters are transmitted. Honors -WhatIf and -Confirm.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
<maml:alert>
|
||||
<maml:para>Renaming or re-slugging an organization may affect billing exports and identity URLs; coordinate with downstream consumers.</maml:para>
|
||||
</maml:alert>
|
||||
</maml:alertSet>
|
||||
<command:examples>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 1</maml:title>
|
||||
<dev:code>Update-InfisicalOrganization -OrganizationId $OrganizationId -Name 'Acme Corp.'</dev:code>
|
||||
<dev:remarks><maml:para>Renames the supplied organization.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 2</maml:title>
|
||||
<dev:code>$UpdateInfisicalOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
$UpdateInfisicalOrganizationParameters.OrganizationId = $OrganizationId
|
||||
$UpdateInfisicalOrganizationParameters.Name = 'Acme Corp.'
|
||||
$UpdateInfisicalOrganizationParameters.Slug = 'acme-corp'
|
||||
$UpdateInfisicalOrganizationParameters.Verbose = $True
|
||||
|
||||
$UpdateInfisicalOrganizationResult = Update-InfisicalOrganization @UpdateInfisicalOrganizationParameters</dev:code>
|
||||
<dev:remarks><maml:para>Renames the organization and updates its slug.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||
<command:details>
|
||||
<command:name>Remove-InfisicalOrganization</command:name>
|
||||
<maml:description><maml:para>Deletes an Infisical organization.</maml:para></maml:description>
|
||||
<command:verb>Remove</command:verb>
|
||||
<command:noun>InfisicalOrganization</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>Deletes an organization by id. -OrganizationId is required. High ConfirmImpact prompts unless -Confirm:$False is supplied. -PassThru emits the removed organization id.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
<maml:alert>
|
||||
<maml:para>This is irreversible and removes all projects, sub-organizations, secrets, and identities owned by the organization. Honors -WhatIf and -Confirm.</maml:para>
|
||||
</maml:alert>
|
||||
</maml:alertSet>
|
||||
<command:examples>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 1</maml:title>
|
||||
<dev:code>Remove-InfisicalOrganization -OrganizationId $OrganizationId -Confirm:$False</dev:code>
|
||||
<dev:remarks><maml:para>Deletes the supplied organization without prompting.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 2</maml:title>
|
||||
<dev:code>$RemoveInfisicalOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
$RemoveInfisicalOrganizationParameters.OrganizationId = $OrganizationId
|
||||
$RemoveInfisicalOrganizationParameters.PassThru = $True
|
||||
$RemoveInfisicalOrganizationParameters.Confirm = $False
|
||||
$RemoveInfisicalOrganizationParameters.Verbose = $True
|
||||
|
||||
$RemoveInfisicalOrganizationResult = Remove-InfisicalOrganization @RemoveInfisicalOrganizationParameters</dev:code>
|
||||
<dev:remarks><maml:para>Removes the organization without confirmation and emits the removed organization id for logging.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||
<command:details>
|
||||
<command:name>Get-InfisicalSubOrganization</command:name>
|
||||
<maml:description><maml:para>Lists or retrieves Infisical sub-organizations accessible to the current identity.</maml:para></maml:description>
|
||||
<command:verb>Get</command:verb>
|
||||
<command:noun>InfisicalSubOrganization</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>Default (List parameter set) returns every sub-organization the active session can see. Optional -Limit, -Offset, -Search, -OrderBy, -OrderDirection, and -IsAccessible are forwarded to the server as query parameters. When -SubOrganizationId is supplied (Single parameter set) the cmdlet returns one sub-organization. Does not require a project context.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
<maml:alert>
|
||||
<maml:para>Sub-organizations are a beta Infisical feature. The List result is an array of InfisicalSubOrganization objects; pipe into Where-Object or Select-Object to filter further. The cmdlet accepts pipeline input by property name on -SubOrganizationId.</maml:para>
|
||||
</maml:alert>
|
||||
</maml:alertSet>
|
||||
<command:examples>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 1</maml:title>
|
||||
<dev:code>Get-InfisicalSubOrganization</dev:code>
|
||||
<dev:remarks><maml:para>Lists every sub-organization the current session can see.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 2</maml:title>
|
||||
<dev:code>Get-InfisicalSubOrganization -SubOrganizationId $SubOrganizationId</dev:code>
|
||||
<dev:remarks><maml:para>Retrieves the canonical record for a single sub-organization by id.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 3</maml:title>
|
||||
<dev:code>Get-InfisicalSubOrganization -Search 'platform' -OrderBy 'name' -OrderDirection 'asc' -Limit 25 -IsAccessible</dev:code>
|
||||
<dev:remarks><maml:para>Lists up to 25 sub-organizations matching 'platform', sorted ascending by name, restricted to those the current identity has access to.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||
<command:details>
|
||||
<command:name>New-InfisicalSubOrganization</command:name>
|
||||
<maml:description><maml:para>Creates a new Infisical sub-organization.</maml:para></maml:description>
|
||||
<command:verb>New</command:verb>
|
||||
<command:noun>InfisicalSubOrganization</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>Creates a sub-organization with the supplied name and slug. Both are required by the server. Honors -WhatIf and -Confirm.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
<maml:alert>
|
||||
<maml:para>Slug must be unique within the parent organization. Sub-organizations are a beta Infisical feature.</maml:para>
|
||||
</maml:alert>
|
||||
</maml:alertSet>
|
||||
<command:examples>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 1</maml:title>
|
||||
<dev:code>New-InfisicalSubOrganization -Name 'Platform Engineering' -Slug 'platform-eng'</dev:code>
|
||||
<dev:remarks><maml:para>Creates a new sub-organization named 'Platform Engineering' with slug 'platform-eng'.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 2</maml:title>
|
||||
<dev:code>$NewInfisicalSubOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
$NewInfisicalSubOrganizationParameters.Name = 'Platform Engineering'
|
||||
$NewInfisicalSubOrganizationParameters.Slug = 'platform-eng'
|
||||
$NewInfisicalSubOrganizationParameters.Verbose = $True
|
||||
|
||||
$NewInfisicalSubOrganizationResult = New-InfisicalSubOrganization @NewInfisicalSubOrganizationParameters</dev:code>
|
||||
<dev:remarks><maml:para>Splatted invocation that creates a sub-organization and logs the request via the verbose stream.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||
<command:details>
|
||||
<command:name>Update-InfisicalSubOrganization</command:name>
|
||||
<maml:description><maml:para>Updates mutable attributes on an existing Infisical sub-organization.</maml:para></maml:description>
|
||||
<command:verb>Update</command:verb>
|
||||
<command:noun>InfisicalSubOrganization</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>Updates the name or slug of a sub-organization. -SubOrganizationId is required. Only bound parameters are transmitted. Honors -WhatIf and -Confirm.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
<maml:alert>
|
||||
<maml:para>Sub-organizations are a beta Infisical feature; coordinate slug changes with downstream consumers that pin the slug in scripts or configuration files.</maml:para>
|
||||
</maml:alert>
|
||||
</maml:alertSet>
|
||||
<command:examples>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 1</maml:title>
|
||||
<dev:code>Update-InfisicalSubOrganization -SubOrganizationId $SubOrganizationId -Name 'Platform (v2)'</dev:code>
|
||||
<dev:remarks><maml:para>Renames the supplied sub-organization.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 2</maml:title>
|
||||
<dev:code>$UpdateInfisicalSubOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
$UpdateInfisicalSubOrganizationParameters.SubOrganizationId = $SubOrganizationId
|
||||
$UpdateInfisicalSubOrganizationParameters.Name = 'Platform (v2)'
|
||||
$UpdateInfisicalSubOrganizationParameters.Slug = 'platform-v2'
|
||||
$UpdateInfisicalSubOrganizationParameters.Verbose = $True
|
||||
|
||||
$UpdateInfisicalSubOrganizationResult = Update-InfisicalSubOrganization @UpdateInfisicalSubOrganizationParameters</dev:code>
|
||||
<dev:remarks><maml:para>Renames the sub-organization and updates its slug in a single call.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||
<command:details>
|
||||
<command:name>Remove-InfisicalSubOrganization</command:name>
|
||||
<maml:description><maml:para>Deletes an Infisical sub-organization.</maml:para></maml:description>
|
||||
<command:verb>Remove</command:verb>
|
||||
<command:noun>InfisicalSubOrganization</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>Deletes a sub-organization by id. -SubOrganizationId is required. High ConfirmImpact prompts unless -Confirm:$False is supplied. -PassThru emits the removed sub-organization id.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
<maml:alert>
|
||||
<maml:para>This is destructive and removes all projects, secrets, and identities scoped to the sub-organization. Honors -WhatIf and -Confirm.</maml:para>
|
||||
</maml:alert>
|
||||
</maml:alertSet>
|
||||
<command:examples>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 1</maml:title>
|
||||
<dev:code>Remove-InfisicalSubOrganization -SubOrganizationId $SubOrganizationId -Confirm:$False</dev:code>
|
||||
<dev:remarks><maml:para>Deletes the supplied sub-organization without prompting.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 2</maml:title>
|
||||
<dev:code>$RemoveInfisicalSubOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
$RemoveInfisicalSubOrganizationParameters.SubOrganizationId = $SubOrganizationId
|
||||
$RemoveInfisicalSubOrganizationParameters.PassThru = $True
|
||||
$RemoveInfisicalSubOrganizationParameters.Confirm = $False
|
||||
$RemoveInfisicalSubOrganizationParameters.Verbose = $True
|
||||
|
||||
$RemoveInfisicalSubOrganizationResult = Remove-InfisicalSubOrganization @RemoveInfisicalSubOrganizationParameters</dev:code>
|
||||
<dev:remarks><maml:para>Removes the sub-organization without confirmation and emits the removed id for logging.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||
<command:details>
|
||||
<command:name>Get-InfisicalSANList</command:name>
|
||||
<maml:description><maml:para>Builds a deduplicated list of Subject Alternative Name candidates for the local device.</maml:para></maml:description>
|
||||
<command:verb>Get</command:verb>
|
||||
<command:noun>InfisicalSANList</command:noun>
|
||||
</command:details>
|
||||
<maml:description>
|
||||
<maml:para>Returns, in order: the local device name; the device name suffixed with each non-empty DNS suffix found on any operational (non-loopback) network adapter and the system primary domain; every IPv4 unicast address whose first octets fall within RFC 1918 (10/8, 172.16/12, 192.168/16) or CGNAT (100.64/10); and the IPv4 and IPv6 loopback addresses (127.0.0.1, ::1). Optional -InclusionExpression and -ExclusionExpression regex filters are applied in that order after collection, before output. Suitable as a one-shot SAN provider for Request-InfisicalCertificate -DnsName.</maml:para>
|
||||
</maml:description>
|
||||
<maml:alertSet>
|
||||
<maml:title>Notes</maml:title>
|
||||
<maml:alert>
|
||||
<maml:para>Output is a single strongly-typed System.String[] array (emitted non-enumerated) so it round-trips into [System.Collections.Generic.List[string]]::AddRange() and binds directly to string[] parameters such as Request-InfisicalCertificate -DnsName. The device name comes first so it can be reused as a CommonName. Routable public IPv4 addresses, link-local addresses, and IPv6 unicast addresses other than loopback are intentionally excluded. -InclusionExpression and -ExclusionExpression are case-insensitive .NET regular expressions; inclusion is applied first, then exclusion.</maml:para>
|
||||
</maml:alert>
|
||||
</maml:alertSet>
|
||||
<command:examples>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 1</maml:title>
|
||||
<dev:code>Get-InfisicalSANList</dev:code>
|
||||
<dev:remarks><maml:para>Returns the SAN candidate list for the current device.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 2</maml:title>
|
||||
<dev:code>$Sans = Get-InfisicalSANList
|
||||
Request-InfisicalCertificate -ProjectId $ProjectId -CertificateAuthorityId $CaId -CommonName $Sans[0] -DnsName $Sans -Ttl '90d'</dev:code>
|
||||
<dev:remarks><maml:para>Captures the SAN list, then uses the device name as the CommonName and the full list as DnsName when requesting a certificate.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
<command:example>
|
||||
<maml:title>EXAMPLE 3</maml:title>
|
||||
<dev:code>$GetInfisicalSANListParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
$GetInfisicalSANListParameters.InclusionExpression = '\.gracesolution\.prv$|^10\.|^172\.'
|
||||
$GetInfisicalSANListParameters.ExclusionExpression = '^127\.|^::1$'
|
||||
$Sans = Get-InfisicalSANList @GetInfisicalSANListParameters</dev:code>
|
||||
<dev:remarks><maml:para>Keeps only entries ending in the corporate DNS suffix or sitting in the 10/8 or 172/12 ranges, then drops loopback. Filters are case-insensitive and applied in fetch -> include -> exclude -> output order.</maml:para></dev:remarks>
|
||||
</command:example>
|
||||
</command:examples>
|
||||
</command:command>
|
||||
|
||||
</helpItems>
|
||||
|
||||
@@ -26,7 +26,7 @@ Import-Module -Name .\Module\PSInfisicalAPI
|
||||
|
||||
## Cmdlets
|
||||
|
||||
The module exports 37 cmdlets. Discovery cmdlets (`Get-Infisical*`) use a `List` (default) / single-record parameter-set pair: invoking without the identity parameter returns the collection, supplying the identity parameter returns one record.
|
||||
The module exports 51 cmdlets. Discovery cmdlets (`Get-Infisical*`) use a `List` (default) / single-record parameter-set pair: invoking without the identity parameter returns the collection, supplying the identity parameter returns one record.
|
||||
|
||||
### Session
|
||||
|
||||
@@ -47,6 +47,24 @@ The module exports 37 cmdlets. Discovery cmdlets (`Get-Infisical*`) use a `List`
|
||||
| `ConvertTo-InfisicalSecretDictionary` | Converts a stream of InfisicalSecret objects into a name-keyed Dictionary of SecureString or plain text values. |
|
||||
| `Export-InfisicalSecrets` | Exports InfisicalSecret objects to disk or environment variables in a chosen file format. |
|
||||
|
||||
### Organizations
|
||||
|
||||
| Cmdlet | Purpose |
|
||||
| ------------------------------ | -------------------------------------------------------------------------------------------------- |
|
||||
| `Get-InfisicalOrganization` | Lists or retrieves Infisical organizations accessible to the current identity. |
|
||||
| `New-InfisicalOrganization` | Creates a new Infisical organization. |
|
||||
| `Update-InfisicalOrganization` | Updates the name or slug of an existing Infisical organization. |
|
||||
| `Remove-InfisicalOrganization` | Deletes an Infisical organization. |
|
||||
|
||||
### Sub-Organizations
|
||||
|
||||
| Cmdlet | Purpose |
|
||||
| --------------------------------- | -------------------------------------------------------------------------------------------------- |
|
||||
| `Get-InfisicalSubOrganization` | Lists or retrieves Infisical sub-organizations, with optional search, paging, and ordering filters. |
|
||||
| `New-InfisicalSubOrganization` | Creates a new Infisical sub-organization. |
|
||||
| `Update-InfisicalSubOrganization` | Updates the name or slug of an existing Infisical sub-organization. |
|
||||
| `Remove-InfisicalSubOrganization` | Deletes an Infisical sub-organization. |
|
||||
|
||||
### Projects
|
||||
|
||||
| Cmdlet | Purpose |
|
||||
@@ -98,6 +116,13 @@ The module exports 37 cmdlets. Discovery cmdlets (`Get-Infisical*`) use a `List`
|
||||
| `Get-InfisicalScepMdmProfile` | Projects an Infisical certificate profile into a Windows SCEP MDM profile model. |
|
||||
| `Export-InfisicalScepMdmProfile` | Writes a SCEP MDM profile to disk as a SyncML payload suitable for MDM delivery. |
|
||||
| `Write-InfisicalScepMdmProfileToWmi`| Submits a SCEP MDM profile to the local MDM Bridge WMI provider to trigger enrollment. |
|
||||
| `Get-InfisicalSANList` | Builds a SAN candidate list (device name, `<device>.<suffix>` per adapter DNS suffix, RFC 1918 + CGNAT IPv4 addresses, IPv4/IPv6 loopback) for `Request-InfisicalCertificate -DnsName`. |
|
||||
|
||||
### Process
|
||||
|
||||
| Cmdlet | Purpose |
|
||||
| ------------------------ | -------------------------------------------------------------------------------------------------- |
|
||||
| `Start-InfisicalProcess` | Launches a child process with Infisical secrets injected directly into its environment block, capturing stdout/stderr and validating the exit code. |
|
||||
|
||||
Use `Get-Help <Cmdlet> -Full` for parameter details and `Get-Help about_PSInfisicalAPI` for the module overview.
|
||||
|
||||
@@ -119,6 +144,40 @@ Get-InfisicalSecret -SecretPath '/'
|
||||
Disconnect-Infisical
|
||||
```
|
||||
|
||||
## End-to-end: request and install a chained certificate
|
||||
|
||||
Connects, selects a project by name, sources SANs from `Get-InfisicalSANList`, picks the first available internal CA, requests a certificate, installs it (and its chain) into the current-user store, and disconnects. Each call uses a splatted `OrderedDictionary` constructed with `OrdinalIgnoreCase` so parameter names round-trip case-insensitively.
|
||||
|
||||
```powershell
|
||||
$ConnectInfisicalParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
$ConnectInfisicalParameters.BaseUri = 'https://app.infisical.com'
|
||||
$ConnectInfisicalParameters.OrganizationId = '00000000-0000-0000-0000-000000000000'
|
||||
$ConnectInfisicalParameters.ClientId = 'machine-identity-client-id'
|
||||
$ConnectInfisicalParameters.ClientSecret = ConvertTo-SecureString -String 'ClientSecret' -AsPlainText -Force
|
||||
$ConnectInfisicalParameters.PassThru = $True
|
||||
$ConnectInfisicalParameters.Verbose = $True
|
||||
|
||||
$Connection = Connect-Infisical @ConnectInfisicalParameters
|
||||
|
||||
$Project = Get-InfisicalProject | Where-Object {($_.Name -eq 'Platform')} | Select-Object -First 1
|
||||
$Ca = Get-InfisicalCertificateAuthority -ProjectId ($Project.Id) | Select-Object -First 1
|
||||
$SanList = Get-InfisicalSANList
|
||||
|
||||
$RequestInfisicalCertificateParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
$RequestInfisicalCertificateParameters.ProjectId = $Project.Id
|
||||
$RequestInfisicalCertificateParameters.CertificateAuthorityId = $Ca.Id
|
||||
$RequestInfisicalCertificateParameters.CommonName = "CN=$($Env:ComputerName.ToUpper())"
|
||||
$RequestInfisicalCertificateParameters.DnsName = New-Object -TypeName 'System.Collections.Generic.List[System.String]'
|
||||
$RequestInfisicalCertificateParameters.DnsName.AddRange($SanList)
|
||||
$RequestInfisicalCertificateParameters.DnsName.Add('myrecord.mydomain.com')
|
||||
$RequestInfisicalCertificateParameters.Ttl = '90d'
|
||||
$RequestInfisicalCertificateParameters.Install = $True
|
||||
$RequestInfisicalCertificateParameters.InstallChain = $True
|
||||
$Certificate = Request-InfisicalCertificate @RequestInfisicalCertificateParameters
|
||||
|
||||
$Null = Disconnect-Infisical -Verbose
|
||||
```
|
||||
|
||||
## Automatic environment-variable discovery
|
||||
|
||||
When `Connect-Infisical` is invoked with one or more parameters missing (or set to whitespace/empty), the cmdlet searches environment variables and uses the first value it finds. This makes invocation as simple as `Connect-Infisical` when variables are set up in advance.
|
||||
|
||||
@@ -129,6 +129,14 @@ function Write-Manifest {
|
||||
'New-InfisicalTag',
|
||||
'Update-InfisicalTag',
|
||||
'Remove-InfisicalTag',
|
||||
'Get-InfisicalOrganization',
|
||||
'New-InfisicalOrganization',
|
||||
'Update-InfisicalOrganization',
|
||||
'Remove-InfisicalOrganization',
|
||||
'Get-InfisicalSubOrganization',
|
||||
'New-InfisicalSubOrganization',
|
||||
'Update-InfisicalSubOrganization',
|
||||
'Remove-InfisicalSubOrganization',
|
||||
'Get-InfisicalCertificateAuthority',
|
||||
'Get-InfisicalPkiSubscriber',
|
||||
'Get-InfisicalCertificateProfile',
|
||||
@@ -144,7 +152,9 @@ function Write-Manifest {
|
||||
'New-InfisicalScepDynamicChallenge',
|
||||
'Get-InfisicalScepMdmProfile',
|
||||
'Export-InfisicalScepMdmProfile',
|
||||
'Write-InfisicalScepMdmProfileToWmi'
|
||||
'Write-InfisicalScepMdmProfileToWmi',
|
||||
'Start-InfisicalProcess',
|
||||
'Get-InfisicalSANList'
|
||||
)
|
||||
AliasesToExport = @()
|
||||
VariablesToExport = @()
|
||||
@@ -209,7 +219,7 @@ if (`$cmds.Count -eq 0) {
|
||||
throw "No cmdlets were exported by the PSInfisicalAPI module."
|
||||
}
|
||||
|
||||
`$expectedCmds = @('Connect-Infisical','Disconnect-Infisical','Get-InfisicalSecret','New-InfisicalSecret','Update-InfisicalSecret','Remove-InfisicalSecret','Copy-InfisicalSecret','ConvertTo-InfisicalSecretDictionary','Export-InfisicalSecrets','Get-InfisicalProject','New-InfisicalProject','Update-InfisicalProject','Remove-InfisicalProject','Get-InfisicalEnvironment','New-InfisicalEnvironment','Update-InfisicalEnvironment','Remove-InfisicalEnvironment','Get-InfisicalFolder','New-InfisicalFolder','Update-InfisicalFolder','Remove-InfisicalFolder','Get-InfisicalTag','New-InfisicalTag','Update-InfisicalTag','Remove-InfisicalTag','Get-InfisicalCertificateAuthority','Get-InfisicalPkiSubscriber','Get-InfisicalCertificateProfile','Get-InfisicalCertificatePolicy','Get-InfisicalCertificate','Request-InfisicalCertificate','ConvertTo-InfisicalCertificate','Install-InfisicalCertificate','Uninstall-InfisicalCertificate','Export-InfisicalCertificate','Get-InfisicalCertificateApplication','Get-InfisicalCertificateApplicationEnrollment','New-InfisicalScepDynamicChallenge','Get-InfisicalScepMdmProfile','Export-InfisicalScepMdmProfile','Write-InfisicalScepMdmProfileToWmi')
|
||||
`$expectedCmds = @('Connect-Infisical','Disconnect-Infisical','Get-InfisicalSecret','New-InfisicalSecret','Update-InfisicalSecret','Remove-InfisicalSecret','Copy-InfisicalSecret','ConvertTo-InfisicalSecretDictionary','Export-InfisicalSecrets','Get-InfisicalProject','New-InfisicalProject','Update-InfisicalProject','Remove-InfisicalProject','Get-InfisicalEnvironment','New-InfisicalEnvironment','Update-InfisicalEnvironment','Remove-InfisicalEnvironment','Get-InfisicalFolder','New-InfisicalFolder','Update-InfisicalFolder','Remove-InfisicalFolder','Get-InfisicalTag','New-InfisicalTag','Update-InfisicalTag','Remove-InfisicalTag','Get-InfisicalOrganization','New-InfisicalOrganization','Update-InfisicalOrganization','Remove-InfisicalOrganization','Get-InfisicalSubOrganization','New-InfisicalSubOrganization','Update-InfisicalSubOrganization','Remove-InfisicalSubOrganization','Get-InfisicalCertificateAuthority','Get-InfisicalPkiSubscriber','Get-InfisicalCertificateProfile','Get-InfisicalCertificatePolicy','Get-InfisicalCertificate','Request-InfisicalCertificate','ConvertTo-InfisicalCertificate','Install-InfisicalCertificate','Uninstall-InfisicalCertificate','Export-InfisicalCertificate','Get-InfisicalCertificateApplication','Get-InfisicalCertificateApplicationEnrollment','New-InfisicalScepDynamicChallenge','Get-InfisicalScepMdmProfile','Export-InfisicalScepMdmProfile','Write-InfisicalScepMdmProfileToWmi','Start-InfisicalProcess','Get-InfisicalSANList')
|
||||
foreach (`$expected in `$expectedCmds) {
|
||||
if (-not (Get-Command -Name `$expected -Module PSInfisicalAPI -ErrorAction SilentlyContinue)) {
|
||||
throw "Cmdlet not found: `$expected"
|
||||
|
||||
+124
-1
@@ -39,6 +39,14 @@ Get-InfisicalTag
|
||||
New-InfisicalTag
|
||||
Update-InfisicalTag
|
||||
Remove-InfisicalTag
|
||||
Get-InfisicalOrganization
|
||||
New-InfisicalOrganization
|
||||
Update-InfisicalOrganization
|
||||
Remove-InfisicalOrganization
|
||||
Get-InfisicalSubOrganization
|
||||
New-InfisicalSubOrganization
|
||||
Update-InfisicalSubOrganization
|
||||
Remove-InfisicalSubOrganization
|
||||
```
|
||||
|
||||
Infisical’s public API is REST-based and provides programmatic access for managing secrets and related resources. Current Infisical documentation shows the list-secrets endpoint under `/api/v4/secrets`, the single-secret retrieval endpoint under `/api/v4/secrets/{secretName}`, and Universal Auth login under `/api/v1/auth/universal-auth/login`. The implementation must centralize API endpoint definitions because Infisical uses different API versions across resource families. ([Infisical Blog][1])
|
||||
@@ -1213,7 +1221,8 @@ Export-InfisicalSecrets `
|
||||
[-Path <FileInfo>] `
|
||||
[-Scope <Process|User|Machine>] `
|
||||
[-Force] `
|
||||
[-Encoding <UTF8|UTF8Bom|Unicode>]
|
||||
[-Encoding <UTF8|UTF8Bom|Unicode>] `
|
||||
[-Prefix <string>]
|
||||
```
|
||||
|
||||
## Parameter Rules
|
||||
@@ -1490,6 +1499,120 @@ No warnings should be emitted.
|
||||
|
||||
---
|
||||
|
||||
# 16.6 Start-InfisicalProcess
|
||||
|
||||
Signature:
|
||||
|
||||
```text
|
||||
Start-InfisicalProcess
|
||||
-FilePath <string>
|
||||
[-WorkingDirectory <DirectoryInfo>]
|
||||
[-ArgumentList <string[]>]
|
||||
[-AcceptableExitCodeList <string[]>]
|
||||
[-WindowStyle <Normal|Hidden|Minimized|Maximized>]
|
||||
[-CreateNoWindow]
|
||||
[-NoWait]
|
||||
[-Priority <AboveNormal|BelowNormal|High|Idle|Normal|RealTime>]
|
||||
[-ExecutionTimeout <TimeSpan>]
|
||||
[-ExecutionTimeoutInterval <TimeSpan>]
|
||||
[-StandardInputObjectList <object[]>]
|
||||
[-EnvironmentVariables <IDictionary>]
|
||||
[-ParsingExpression <Regex>]
|
||||
[-SecureArgumentList]
|
||||
[-LogOutput]
|
||||
[-ContinueOnError]
|
||||
[-Secret <InfisicalSecret[]>]
|
||||
[-Prefix <string>]
|
||||
```
|
||||
|
||||
Behavior:
|
||||
|
||||
```text
|
||||
Buffer pipeline InfisicalSecret objects in ProcessRecord.
|
||||
Decrypt secrets only into ProcessStartInfo.Environment.
|
||||
Apply -Prefix to each secret name before injection.
|
||||
Never write secret plaintext to user or machine environment scope.
|
||||
Honor -WhatIf / -Confirm.
|
||||
Default -AcceptableExitCodeList = @('0','3010').
|
||||
Throw a terminating error on unacceptable exit code unless -ContinueOnError is set.
|
||||
```
|
||||
|
||||
Output: `InfisicalProcessResult` with `ExitCode`, `ExitCodeAsHex`, `ExitCodeAsInteger`, `ExitCodeAsDecimal`, `StandardOutput`, `StandardError`, `StandardOutputObject`, `StandardErrorObject`, `StartTime`, `ExitTime`, `Duration`, `DurationFriendly`, `ProcessId`, `TimedOut`, `Succeeded`, `SecretCount`.
|
||||
|
||||
---
|
||||
|
||||
# 16.7 Organization Cmdlets
|
||||
|
||||
Organizations are the top-level tenancy boundary in Infisical. They are not scoped under a project; the active connection's `OrganizationId` is used as the default identifier when an explicit one is not supplied.
|
||||
|
||||
Cmdlet signatures:
|
||||
|
||||
```powershell
|
||||
Get-InfisicalOrganization [[-OrganizationId] <string>] # default = List
|
||||
New-InfisicalOrganization [-Name] <string> [-Slug <string>] [-WhatIf] [-Confirm]
|
||||
Update-InfisicalOrganization [-OrganizationId] <string> [-Name <string>] [-Slug <string>] [-WhatIf] [-Confirm]
|
||||
Remove-InfisicalOrganization [-OrganizationId] <string> [-PassThru] [-WhatIf] [-Confirm]
|
||||
```
|
||||
|
||||
Parameter sets:
|
||||
|
||||
| Cmdlet | Default set | Single set | Notes |
|
||||
|---|---|---|---|
|
||||
| `Get-InfisicalOrganization` | `List` (no `-Id`) | `Single` (`-OrganizationId`/`-Id`) | No `-ProjectId`. |
|
||||
| `New-InfisicalOrganization` | n/a | `-Name` mandatory, `-Slug` optional | ShouldProcess. |
|
||||
| `Update-InfisicalOrganization` | n/a | `-OrganizationId` mandatory | ShouldProcess; only bound parameters are sent. |
|
||||
| `Remove-InfisicalOrganization` | n/a | `-OrganizationId` mandatory | `ConfirmImpact.High`; `-PassThru` emits removed id. |
|
||||
|
||||
Endpoints:
|
||||
|
||||
| Operation | Method | Template | Version |
|
||||
|---|---|---|---|
|
||||
| List | `GET` | `/api/v2/organizations` | v2 |
|
||||
| Retrieve | `GET` | `/api/v1/organization/{organizationId}` | v1 |
|
||||
| Create | `POST` | `/api/v2/organizations` | v2 |
|
||||
| Update | `PATCH` | `/api/v1/organization/{organizationId}` | v1 |
|
||||
| Delete | `DELETE` | `/api/v1/organization/{organizationId}` | v1 |
|
||||
|
||||
Output: `InfisicalOrganization` with `Id`, `Name`, `Slug`, `CustomerId`, `AuthEnforced`, `ScimEnabled`, `CreatedAtUtc`, `UpdatedAtUtc`.
|
||||
|
||||
---
|
||||
|
||||
# 16.8 Sub-Organization Cmdlets
|
||||
|
||||
Sub-organizations partition an organization into isolated child tenants. They are not scoped under a project; the active connection is used for the parent organization context.
|
||||
|
||||
Cmdlet signatures:
|
||||
|
||||
```powershell
|
||||
Get-InfisicalSubOrganization [[-SubOrganizationId] <string>] [-Limit <int>] [-Offset <int>] [-Search <string>] [-OrderBy <string>] [-OrderDirection <string>] [-IsAccessible]
|
||||
New-InfisicalSubOrganization [-Name] <string> [-Slug] <string> [-WhatIf] [-Confirm]
|
||||
Update-InfisicalSubOrganization [-SubOrganizationId] <string> [-Name <string>] [-Slug <string>] [-WhatIf] [-Confirm]
|
||||
Remove-InfisicalSubOrganization [-SubOrganizationId] <string> [-PassThru] [-WhatIf] [-Confirm]
|
||||
```
|
||||
|
||||
Parameter sets:
|
||||
|
||||
| Cmdlet | Default set | Single set | Notes |
|
||||
|---|---|---|---|
|
||||
| `Get-InfisicalSubOrganization` | `List` (no `-Id`) | `Single` (`-SubOrganizationId`/`-Id`) | List supports server-side `-Limit`, `-Offset`, `-Search`, `-OrderBy`, `-OrderDirection`, `-IsAccessible`. |
|
||||
| `New-InfisicalSubOrganization` | n/a | `-Name` + `-Slug` mandatory | ShouldProcess. |
|
||||
| `Update-InfisicalSubOrganization` | n/a | `-SubOrganizationId` mandatory | ShouldProcess; only bound parameters are sent. |
|
||||
| `Remove-InfisicalSubOrganization` | n/a | `-SubOrganizationId` mandatory | `ConfirmImpact.High`; `-PassThru` emits removed id. |
|
||||
|
||||
Endpoints (beta):
|
||||
|
||||
| Operation | Method | Template | Version |
|
||||
|---|---|---|---|
|
||||
| List | `GET` | `/api/v1/sub-organizations` | v1 |
|
||||
| Retrieve | `GET` | `/api/v1/sub-organizations/{subOrgId}` | v1 |
|
||||
| Create | `POST` | `/api/v1/sub-organizations` | v1 |
|
||||
| Update | `PATCH` | `/api/v1/sub-organizations/{subOrgId}` | v1 |
|
||||
| Delete | `DELETE` | `/api/v1/sub-organizations/{subOrgId}` | v1 |
|
||||
|
||||
Output: `InfisicalSubOrganization` with `Id`, `Name`, `Slug`, `OrganizationId`, `IsAccessible`, `CreatedAtUtc`, `UpdatedAtUtc`.
|
||||
|
||||
---
|
||||
|
||||
# 17. SecureString Utility
|
||||
|
||||
Required utility:
|
||||
|
||||
@@ -84,5 +84,38 @@ namespace PSInfisicalAPI.Tests
|
||||
Assert.Equal("explicit-org", cmdlet.CallResolveOrganizationId(ConnectionWithDefaults(), "explicit-org"));
|
||||
Assert.Empty(logger.VerboseEntries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InfisicalConnection_Defaults_TransportFlags_To_False()
|
||||
{
|
||||
InfisicalConnection connection = new InfisicalConnection();
|
||||
Assert.False(connection.SkipCertificateCheck);
|
||||
Assert.False(connection.AllowInsecureTransport);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldSkipCertificateCheck_Reads_From_Current_Session()
|
||||
{
|
||||
InfisicalConnection previous = InfisicalSessionManager.Current;
|
||||
try
|
||||
{
|
||||
TestCmdlet cmdlet = CreateCmdletWith(new RecordingLogger());
|
||||
MethodInfo virt = typeof(InfisicalCmdletBase).GetMethod("ShouldSkipCertificateCheck", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
InfisicalSessionManager.SetCurrent(null);
|
||||
Assert.False((bool)virt.Invoke(cmdlet, null));
|
||||
|
||||
InfisicalConnection session = ConnectionWithDefaults();
|
||||
session.IsConnected = true;
|
||||
session.SkipCertificateCheck = true;
|
||||
InfisicalSessionManager.SetCurrent(session);
|
||||
|
||||
Assert.True((bool)virt.Invoke(cmdlet, null));
|
||||
}
|
||||
finally
|
||||
{
|
||||
InfisicalSessionManager.SetCurrent(previous);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,16 @@ namespace PSInfisicalAPI.Tests
|
||||
[InlineData(InfisicalEndpointNames.BulkUpdateSecret, "PATCH", "/api/v4/secrets/batch")]
|
||||
[InlineData(InfisicalEndpointNames.BulkDeleteSecret, "DELETE", "/api/v4/secrets/batch")]
|
||||
[InlineData(InfisicalEndpointNames.DuplicateSecret, "POST", "/api/v4/secrets/duplicate")]
|
||||
[InlineData(InfisicalEndpointNames.ListOrganizations, "GET", "/api/v2/organizations")]
|
||||
[InlineData(InfisicalEndpointNames.RetrieveOrganization, "GET", "/api/v1/organization/{organizationId}")]
|
||||
[InlineData(InfisicalEndpointNames.CreateOrganization, "POST", "/api/v2/organizations")]
|
||||
[InlineData(InfisicalEndpointNames.UpdateOrganization, "PATCH", "/api/v1/organization/{organizationId}")]
|
||||
[InlineData(InfisicalEndpointNames.DeleteOrganization, "DELETE", "/api/v1/organization/{organizationId}")]
|
||||
[InlineData(InfisicalEndpointNames.ListSubOrganizations, "GET", "/api/v1/sub-organizations")]
|
||||
[InlineData(InfisicalEndpointNames.RetrieveSubOrganization, "GET", "/api/v1/sub-organizations/{subOrgId}")]
|
||||
[InlineData(InfisicalEndpointNames.CreateSubOrganization, "POST", "/api/v1/sub-organizations")]
|
||||
[InlineData(InfisicalEndpointNames.UpdateSubOrganization, "PATCH", "/api/v1/sub-organizations/{subOrgId}")]
|
||||
[InlineData(InfisicalEndpointNames.DeleteSubOrganization, "DELETE", "/api/v1/sub-organizations/{subOrgId}")]
|
||||
public void Registered_Endpoints_Have_Expected_Shape(string name, string method, string template)
|
||||
{
|
||||
InfisicalEndpointDefinition definition = InfisicalEndpointRegistry.Get(name);
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using PSInfisicalAPI.Process;
|
||||
using Xunit;
|
||||
|
||||
namespace PSInfisicalAPI.Tests
|
||||
{
|
||||
public class InfisicalProcessRunnerHelpersTests
|
||||
{
|
||||
[Fact]
|
||||
public void FormatFriendly_Zero_Returns_NotAvailable()
|
||||
{
|
||||
Assert.Equal("N/A", InfisicalProcessRunnerHelpers.FormatFriendly(TimeSpan.Zero));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormatFriendly_Single_Unit_Plural()
|
||||
{
|
||||
Assert.Equal("30 seconds", InfisicalProcessRunnerHelpers.FormatFriendly(TimeSpan.FromSeconds(30)));
|
||||
Assert.Equal("5 minutes", InfisicalProcessRunnerHelpers.FormatFriendly(TimeSpan.FromMinutes(5)));
|
||||
Assert.Equal("250 milliseconds", InfisicalProcessRunnerHelpers.FormatFriendly(TimeSpan.FromMilliseconds(250)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormatFriendly_Single_Unit_Singular()
|
||||
{
|
||||
Assert.Equal("1 second", InfisicalProcessRunnerHelpers.FormatFriendly(TimeSpan.FromSeconds(1)));
|
||||
Assert.Equal("1 minute", InfisicalProcessRunnerHelpers.FormatFriendly(TimeSpan.FromMinutes(1)));
|
||||
Assert.Equal("1 hour", InfisicalProcessRunnerHelpers.FormatFriendly(TimeSpan.FromHours(1)));
|
||||
Assert.Equal("1 day", InfisicalProcessRunnerHelpers.FormatFriendly(TimeSpan.FromDays(1)));
|
||||
Assert.Equal("1 millisecond", InfisicalProcessRunnerHelpers.FormatFriendly(TimeSpan.FromMilliseconds(1)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormatFriendly_Two_Units_Uses_And_Join()
|
||||
{
|
||||
TimeSpan value = TimeSpan.FromSeconds(7) + TimeSpan.FromMilliseconds(364);
|
||||
Assert.Equal("7 seconds, and 364 milliseconds", InfisicalProcessRunnerHelpers.FormatFriendly(value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormatFriendly_Multiple_Units_Uses_Comma_And_Trailing_And()
|
||||
{
|
||||
TimeSpan value = TimeSpan.FromHours(1) + TimeSpan.FromMinutes(2) + TimeSpan.FromSeconds(3) + TimeSpan.FromMilliseconds(45);
|
||||
Assert.Equal("1 hour, 2 minutes, 3 seconds, and 45 milliseconds", InfisicalProcessRunnerHelpers.FormatFriendly(value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormatFriendly_Skips_Zero_Components()
|
||||
{
|
||||
TimeSpan value = TimeSpan.FromHours(2) + TimeSpan.FromMilliseconds(500);
|
||||
Assert.Equal("2 hours, and 500 milliseconds", InfisicalProcessRunnerHelpers.FormatFriendly(value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormatFriendly_Mixed_Singular_And_Plural()
|
||||
{
|
||||
TimeSpan value = TimeSpan.FromMinutes(1) + TimeSpan.FromSeconds(30);
|
||||
Assert.Equal("1 minute, and 30 seconds", InfisicalProcessRunnerHelpers.FormatFriendly(value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormatFriendly_Days_Component()
|
||||
{
|
||||
TimeSpan value = TimeSpan.FromDays(2) + TimeSpan.FromHours(3);
|
||||
Assert.Equal("2 days, and 3 hours", InfisicalProcessRunnerHelpers.FormatFriendly(value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormatFriendly_SubMillisecond_Returns_NotAvailable()
|
||||
{
|
||||
TimeSpan value = TimeSpan.FromTicks(100);
|
||||
Assert.Equal("N/A", InfisicalProcessRunnerHelpers.FormatFriendly(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using System.Reflection;
|
||||
using PSInfisicalAPI.Models;
|
||||
using Xunit;
|
||||
|
||||
namespace PSInfisicalAPI.Tests
|
||||
{
|
||||
public class OrganizationMapperTests
|
||||
{
|
||||
private static readonly System.Type MapperType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly
|
||||
.GetType("PSInfisicalAPI.Organizations.InfisicalOrganizationMapper", true);
|
||||
|
||||
private static readonly System.Type DtoType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly
|
||||
.GetType("PSInfisicalAPI.Organizations.InfisicalOrganizationResponseDto", true);
|
||||
|
||||
private static InfisicalOrganization InvokeMap(object dto)
|
||||
{
|
||||
MethodInfo map = MapperType.GetMethod("Map", BindingFlags.Public | BindingFlags.Static);
|
||||
return (InfisicalOrganization)map.Invoke(null, new[] { dto });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Map_Null_Dto_Returns_Null()
|
||||
{
|
||||
Assert.Null(InvokeMap(null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Map_Populates_Core_Fields()
|
||||
{
|
||||
object dto = System.Activator.CreateInstance(DtoType);
|
||||
DtoType.GetProperty("Id").SetValue(dto, "org-001");
|
||||
DtoType.GetProperty("Name").SetValue(dto, "Acme");
|
||||
DtoType.GetProperty("Slug").SetValue(dto, "acme");
|
||||
DtoType.GetProperty("CustomerId").SetValue(dto, "cust-9");
|
||||
DtoType.GetProperty("AuthEnforced").SetValue(dto, true);
|
||||
DtoType.GetProperty("ScimEnabled").SetValue(dto, true);
|
||||
DtoType.GetProperty("CreatedAt").SetValue(dto, "2026-01-15T12:34:56Z");
|
||||
DtoType.GetProperty("UpdatedAt").SetValue(dto, "2026-02-20T09:00:00Z");
|
||||
|
||||
InfisicalOrganization organization = InvokeMap(dto);
|
||||
|
||||
Assert.Equal("org-001", organization.Id);
|
||||
Assert.Equal("Acme", organization.Name);
|
||||
Assert.Equal("acme", organization.Slug);
|
||||
Assert.Equal("cust-9", organization.CustomerId);
|
||||
Assert.True(organization.AuthEnforced);
|
||||
Assert.True(organization.ScimEnabled);
|
||||
Assert.NotNull(organization.CreatedAtUtc);
|
||||
Assert.NotNull(organization.UpdatedAtUtc);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Map_Falls_Back_To_InternalId()
|
||||
{
|
||||
object dto = System.Activator.CreateInstance(DtoType);
|
||||
DtoType.GetProperty("InternalId").SetValue(dto, "internal-id-1");
|
||||
|
||||
InfisicalOrganization organization = InvokeMap(dto);
|
||||
|
||||
Assert.Equal("internal-id-1", organization.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MapMany_Null_Returns_Empty()
|
||||
{
|
||||
MethodInfo mapMany = MapperType.GetMethod("MapMany", BindingFlags.Public | BindingFlags.Static);
|
||||
InfisicalOrganization[] result = (InfisicalOrganization[])mapMany.Invoke(null, new object[] { null });
|
||||
Assert.NotNull(result);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using System.Reflection;
|
||||
using PSInfisicalAPI.Models;
|
||||
using Xunit;
|
||||
|
||||
namespace PSInfisicalAPI.Tests
|
||||
{
|
||||
public class SubOrganizationMapperTests
|
||||
{
|
||||
private static readonly System.Type MapperType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly
|
||||
.GetType("PSInfisicalAPI.SubOrganizations.InfisicalSubOrganizationMapper", true);
|
||||
|
||||
private static readonly System.Type DtoType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly
|
||||
.GetType("PSInfisicalAPI.SubOrganizations.InfisicalSubOrganizationResponseDto", true);
|
||||
|
||||
private static InfisicalSubOrganization InvokeMap(object dto)
|
||||
{
|
||||
MethodInfo map = MapperType.GetMethod("Map", BindingFlags.Public | BindingFlags.Static);
|
||||
return (InfisicalSubOrganization)map.Invoke(null, new[] { dto });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Map_Null_Dto_Returns_Null()
|
||||
{
|
||||
Assert.Null(InvokeMap(null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Map_Populates_Core_Fields()
|
||||
{
|
||||
object dto = System.Activator.CreateInstance(DtoType);
|
||||
DtoType.GetProperty("Id").SetValue(dto, "sub-001");
|
||||
DtoType.GetProperty("Name").SetValue(dto, "Platform Engineering");
|
||||
DtoType.GetProperty("Slug").SetValue(dto, "platform-eng");
|
||||
DtoType.GetProperty("OrganizationId").SetValue(dto, "org-001");
|
||||
DtoType.GetProperty("IsAccessible").SetValue(dto, true);
|
||||
DtoType.GetProperty("CreatedAt").SetValue(dto, "2026-01-15T12:34:56Z");
|
||||
DtoType.GetProperty("UpdatedAt").SetValue(dto, "2026-02-20T09:00:00Z");
|
||||
|
||||
InfisicalSubOrganization subOrganization = InvokeMap(dto);
|
||||
|
||||
Assert.Equal("sub-001", subOrganization.Id);
|
||||
Assert.Equal("Platform Engineering", subOrganization.Name);
|
||||
Assert.Equal("platform-eng", subOrganization.Slug);
|
||||
Assert.Equal("org-001", subOrganization.OrganizationId);
|
||||
Assert.True(subOrganization.IsAccessible);
|
||||
Assert.NotNull(subOrganization.CreatedAtUtc);
|
||||
Assert.NotNull(subOrganization.UpdatedAtUtc);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Map_Falls_Back_To_InternalId_And_OrgId()
|
||||
{
|
||||
object dto = System.Activator.CreateInstance(DtoType);
|
||||
DtoType.GetProperty("InternalId").SetValue(dto, "internal-id-1");
|
||||
DtoType.GetProperty("OrgId").SetValue(dto, "org-fallback");
|
||||
|
||||
InfisicalSubOrganization subOrganization = InvokeMap(dto);
|
||||
|
||||
Assert.Equal("internal-id-1", subOrganization.Id);
|
||||
Assert.Equal("org-fallback", subOrganization.OrganizationId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MapMany_Null_Returns_Empty()
|
||||
{
|
||||
MethodInfo mapMany = MapperType.GetMethod("MapMany", BindingFlags.Public | BindingFlags.Static);
|
||||
InfisicalSubOrganization[] result = (InfisicalSubOrganization[])mapMany.Invoke(null, new object[] { null });
|
||||
Assert.NotNull(result);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,12 +62,24 @@ namespace PSInfisicalAPI.Cmdlets
|
||||
[Parameter]
|
||||
public SwitchParameter PassThru { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public SwitchParameter SkipCertificateCheck { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public SwitchParameter AllowInsecureTransport { get; set; }
|
||||
|
||||
protected override bool ShouldSkipCertificateCheck()
|
||||
{
|
||||
return SkipCertificateCheck.IsPresent;
|
||||
}
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
try
|
||||
{
|
||||
ResolveMissingParametersFromEnvironment();
|
||||
ValidateRequiredParameters();
|
||||
ValidateTransportSafety();
|
||||
|
||||
IInfisicalAuthProvider provider;
|
||||
InfisicalAuthenticationRequest request;
|
||||
@@ -179,7 +191,9 @@ namespace PSInfisicalAPI.Cmdlets
|
||||
ConnectedAtUtc = DateTimeOffset.UtcNow,
|
||||
ExpiresAtUtc = authResult.ExpiresAtUtc,
|
||||
IsConnected = true,
|
||||
AccessToken = authResult.AccessToken
|
||||
AccessToken = authResult.AccessToken,
|
||||
SkipCertificateCheck = SkipCertificateCheck.IsPresent,
|
||||
AllowInsecureTransport = AllowInsecureTransport.IsPresent
|
||||
};
|
||||
|
||||
InfisicalSessionManager.SetCurrent(connection);
|
||||
@@ -195,6 +209,26 @@ namespace PSInfisicalAPI.Cmdlets
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateTransportSafety()
|
||||
{
|
||||
bool isHttp = BaseUri != null && string.Equals(BaseUri.Scheme, "http", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (isHttp && !AllowInsecureTransport.IsPresent)
|
||||
{
|
||||
throw new InfisicalConfigurationException("BaseUri '" + BaseUri + "' is not HTTPS. Re-run Connect-Infisical with -AllowInsecureTransport to permit plaintext.");
|
||||
}
|
||||
|
||||
if (SkipCertificateCheck.IsPresent)
|
||||
{
|
||||
Logger.Warning(Component, "SkipCertificateCheck is enabled. TLS certificate validation is disabled for this session. Do not use in production.");
|
||||
}
|
||||
|
||||
if (AllowInsecureTransport.IsPresent && isHttp)
|
||||
{
|
||||
Logger.Warning(Component, "AllowInsecureTransport is enabled and BaseUri uses HTTP. Credentials and secrets will traverse the network unencrypted. Do not use in production.");
|
||||
}
|
||||
}
|
||||
|
||||
private void ResolveMissingParametersFromEnvironment()
|
||||
{
|
||||
bool tokenSet = string.Equals(ParameterSetName, ParameterSetToken, StringComparison.Ordinal);
|
||||
|
||||
@@ -37,6 +37,9 @@ namespace PSInfisicalAPI.Cmdlets
|
||||
[Parameter]
|
||||
public InfisicalExportEncoding Encoding { get; set; } = InfisicalExportEncoding.UTF8;
|
||||
|
||||
[Parameter]
|
||||
public string Prefix { get; set; }
|
||||
|
||||
private readonly List<InfisicalSecret> _buffer = new List<InfisicalSecret>();
|
||||
|
||||
protected override void ProcessRecord()
|
||||
@@ -68,7 +71,7 @@ namespace PSInfisicalAPI.Cmdlets
|
||||
|
||||
InfisicalExportRequest request = new InfisicalExportRequest
|
||||
{
|
||||
Secrets = _buffer.ToArray(),
|
||||
Secrets = ApplyPrefix(_buffer, Prefix),
|
||||
Format = Format,
|
||||
Path = Path,
|
||||
Scope = Scope,
|
||||
@@ -85,6 +88,38 @@ namespace PSInfisicalAPI.Cmdlets
|
||||
}
|
||||
}
|
||||
|
||||
private static InfisicalSecret[] ApplyPrefix(List<InfisicalSecret> source, string prefix)
|
||||
{
|
||||
if (string.IsNullOrEmpty(prefix)) { return source.ToArray(); }
|
||||
|
||||
InfisicalSecret[] result = new InfisicalSecret[source.Count];
|
||||
for (int i = 0; i < source.Count; i++)
|
||||
{
|
||||
InfisicalSecret original = source[i];
|
||||
result[i] = new InfisicalSecret
|
||||
{
|
||||
Id = original.Id,
|
||||
InternalId = original.InternalId,
|
||||
Workspace = original.Workspace,
|
||||
Environment = original.Environment,
|
||||
Version = original.Version,
|
||||
Type = original.Type,
|
||||
SecretName = string.Concat(prefix, original.SecretName),
|
||||
SecretValue = original.SecretValue,
|
||||
SecretValueHidden = original.SecretValueHidden,
|
||||
SecretPath = original.SecretPath,
|
||||
SecretComment = original.SecretComment,
|
||||
CreatedAtUtc = original.CreatedAtUtc,
|
||||
UpdatedAtUtc = original.UpdatedAtUtc,
|
||||
IsRotatedSecret = original.IsRotatedSecret,
|
||||
RotationId = original.RotationId,
|
||||
Tags = original.Tags,
|
||||
SecretMetadata = original.SecretMetadata
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Encoding ResolveEncoding(InfisicalExportEncoding encoding)
|
||||
{
|
||||
switch (encoding)
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSInfisicalAPI.Connections;
|
||||
using PSInfisicalAPI.Models;
|
||||
using PSInfisicalAPI.Organizations;
|
||||
|
||||
namespace PSInfisicalAPI.Cmdlets
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Get, "InfisicalOrganization", DefaultParameterSetName = "List")]
|
||||
[OutputType(typeof(InfisicalOrganization))]
|
||||
public sealed class GetInfisicalOrganizationCmdlet : InfisicalCmdletBase
|
||||
{
|
||||
[Parameter(ParameterSetName = "Single", Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
|
||||
[Alias("Id")]
|
||||
public string OrganizationId { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
try
|
||||
{
|
||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||
InfisicalOrganizationClient client = new InfisicalOrganizationClient(HttpClient, Logger);
|
||||
|
||||
if (string.Equals(ParameterSetName, "Single", StringComparison.Ordinal))
|
||||
{
|
||||
InfisicalOrganization organization = client.Retrieve(connection, OrganizationId);
|
||||
if (organization != null)
|
||||
{
|
||||
WriteObject(organization);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
InfisicalOrganization[] organizations = client.List(connection);
|
||||
foreach (InfisicalOrganization organization in organizations)
|
||||
{
|
||||
WriteObject(organization);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
ThrowTerminatingForException("GetInfisicalOrganizationCmdlet", "GetOrganization", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Management.Automation;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace PSInfisicalAPI.Cmdlets
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Get, "InfisicalSANList")]
|
||||
[OutputType(typeof(string[]))]
|
||||
public sealed class GetInfisicalSANListCmdlet : InfisicalCmdletBase
|
||||
{
|
||||
private const string Component = "GetInfisicalSANListCmdlet";
|
||||
|
||||
[Parameter]
|
||||
[ValidateNotNullOrEmpty]
|
||||
public string InclusionExpression { get; set; }
|
||||
|
||||
[Parameter]
|
||||
[ValidateNotNullOrEmpty]
|
||||
public string ExclusionExpression { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
try
|
||||
{
|
||||
Regex includeRegex = !string.IsNullOrEmpty(InclusionExpression) ? new Regex(InclusionExpression, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) : null;
|
||||
Regex excludeRegex = !string.IsNullOrEmpty(ExclusionExpression) ? new Regex(ExclusionExpression, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) : null;
|
||||
|
||||
List<string> sans = new List<string>();
|
||||
HashSet<string> seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
string deviceName = Dns.GetHostName();
|
||||
AddUnique(sans, seen, deviceName);
|
||||
|
||||
HashSet<string> suffixes = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
string globalDomain = IPGlobalProperties.GetIPGlobalProperties().DomainName;
|
||||
if (!string.IsNullOrEmpty(globalDomain)) { suffixes.Add(globalDomain); }
|
||||
|
||||
NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces();
|
||||
foreach (NetworkInterface adapter in adapters)
|
||||
{
|
||||
if (adapter.OperationalStatus != OperationalStatus.Up) { continue; }
|
||||
if (adapter.NetworkInterfaceType == NetworkInterfaceType.Loopback) { continue; }
|
||||
|
||||
IPInterfaceProperties props = adapter.GetIPProperties();
|
||||
if (!string.IsNullOrEmpty(props.DnsSuffix)) { suffixes.Add(props.DnsSuffix); }
|
||||
|
||||
foreach (UnicastIPAddressInformation unicast in props.UnicastAddresses)
|
||||
{
|
||||
IPAddress ip = unicast.Address;
|
||||
if (ip.AddressFamily == AddressFamily.InterNetwork && IsRfc1918OrCgnat(ip))
|
||||
{
|
||||
AddUnique(sans, seen, ip.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (string suffix in suffixes)
|
||||
{
|
||||
string trimmed = suffix.Trim().TrimStart('.');
|
||||
if (!string.IsNullOrEmpty(trimmed))
|
||||
{
|
||||
AddUnique(sans, seen, string.Concat(deviceName, ".", trimmed));
|
||||
}
|
||||
}
|
||||
|
||||
AddUnique(sans, seen, "127.0.0.1");
|
||||
AddUnique(sans, seen, "::1");
|
||||
|
||||
List<string> filtered = new List<string>(sans.Count);
|
||||
foreach (string san in sans)
|
||||
{
|
||||
if (includeRegex != null && !includeRegex.IsMatch(san)) { continue; }
|
||||
if (excludeRegex != null && excludeRegex.IsMatch(san)) { continue; }
|
||||
filtered.Add(san);
|
||||
}
|
||||
|
||||
WriteObject(filtered.ToArray(), false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
ThrowTerminatingForException(Component, "GetSANList", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsRfc1918OrCgnat(IPAddress ip)
|
||||
{
|
||||
byte[] bytes = ip.GetAddressBytes();
|
||||
if (bytes.Length != 4) { return false; }
|
||||
|
||||
if (bytes[0] == 10) { return true; }
|
||||
if (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31) { return true; }
|
||||
if (bytes[0] == 192 && bytes[1] == 168) { return true; }
|
||||
if (bytes[0] == 100 && bytes[1] >= 64 && bytes[1] <= 127) { return true; }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void AddUnique(List<string> list, HashSet<string> seen, string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value)) { return; }
|
||||
if (seen.Add(value)) { list.Add(value); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSInfisicalAPI.Connections;
|
||||
using PSInfisicalAPI.Models;
|
||||
using PSInfisicalAPI.SubOrganizations;
|
||||
|
||||
namespace PSInfisicalAPI.Cmdlets
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Get, "InfisicalSubOrganization", DefaultParameterSetName = "List")]
|
||||
[OutputType(typeof(InfisicalSubOrganization))]
|
||||
public sealed class GetInfisicalSubOrganizationCmdlet : InfisicalCmdletBase
|
||||
{
|
||||
[Parameter(ParameterSetName = "Single", Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
|
||||
[Alias("Id")]
|
||||
public string SubOrganizationId { get; set; }
|
||||
|
||||
[Parameter(ParameterSetName = "List")] public int? Limit { get; set; }
|
||||
[Parameter(ParameterSetName = "List")] public int? Offset { get; set; }
|
||||
[Parameter(ParameterSetName = "List")] public string Search { get; set; }
|
||||
[Parameter(ParameterSetName = "List")] public string OrderBy { get; set; }
|
||||
|
||||
[Parameter(ParameterSetName = "List")]
|
||||
[ValidateSet("asc", "desc")]
|
||||
public string OrderDirection { get; set; }
|
||||
|
||||
[Parameter(ParameterSetName = "List")] public SwitchParameter IsAccessible { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
try
|
||||
{
|
||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||
InfisicalSubOrganizationClient client = new InfisicalSubOrganizationClient(HttpClient, Logger);
|
||||
|
||||
if (string.Equals(ParameterSetName, "Single", StringComparison.Ordinal))
|
||||
{
|
||||
InfisicalSubOrganization subOrganization = client.Retrieve(connection, SubOrganizationId);
|
||||
if (subOrganization != null)
|
||||
{
|
||||
WriteObject(subOrganization);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool? isAccessible = MyInvocation.BoundParameters.ContainsKey("IsAccessible") ? (bool?)IsAccessible.IsPresent : null;
|
||||
InfisicalSubOrganization[] subOrganizations = client.List(connection, Limit, Offset, Search, OrderBy, OrderDirection, isAccessible);
|
||||
foreach (InfisicalSubOrganization subOrganization in subOrganizations)
|
||||
{
|
||||
WriteObject(subOrganization);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
ThrowTerminatingForException("GetInfisicalSubOrganizationCmdlet", "GetSubOrganization", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,13 +31,19 @@ namespace PSInfisicalAPI.Cmdlets
|
||||
{
|
||||
if (_httpClient == null)
|
||||
{
|
||||
_httpClient = new InfisicalHttpClient(Logger);
|
||||
_httpClient = new InfisicalHttpClient(Logger, 100, ShouldSkipCertificateCheck());
|
||||
}
|
||||
|
||||
return _httpClient;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual bool ShouldSkipCertificateCheck()
|
||||
{
|
||||
InfisicalConnection current = InfisicalSessionManager.Current;
|
||||
return current != null && current.SkipCertificateCheck;
|
||||
}
|
||||
|
||||
protected void ThrowTerminatingForException(string component, string operation, Exception exception)
|
||||
{
|
||||
InfisicalErrorDetails details = InfisicalErrorHandler.BuildDetails(component, operation, exception);
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSInfisicalAPI.Connections;
|
||||
using PSInfisicalAPI.Models;
|
||||
using PSInfisicalAPI.Organizations;
|
||||
|
||||
namespace PSInfisicalAPI.Cmdlets
|
||||
{
|
||||
[Cmdlet(VerbsCommon.New, "InfisicalOrganization", SupportsShouldProcess = true)]
|
||||
[OutputType(typeof(InfisicalOrganization))]
|
||||
public sealed class NewInfisicalOrganizationCmdlet : InfisicalCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0)] public string Name { get; set; }
|
||||
[Parameter] public string Slug { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ShouldProcess(Name, "Create Infisical organization"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||
InfisicalOrganizationClient client = new InfisicalOrganizationClient(HttpClient, Logger);
|
||||
InfisicalOrganization organization = client.Create(connection, Name, Slug);
|
||||
if (organization != null)
|
||||
{
|
||||
WriteObject(organization);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
ThrowTerminatingForException("NewInfisicalOrganizationCmdlet", "CreateOrganization", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSInfisicalAPI.Connections;
|
||||
using PSInfisicalAPI.Models;
|
||||
using PSInfisicalAPI.SubOrganizations;
|
||||
|
||||
namespace PSInfisicalAPI.Cmdlets
|
||||
{
|
||||
[Cmdlet(VerbsCommon.New, "InfisicalSubOrganization", SupportsShouldProcess = true)]
|
||||
[OutputType(typeof(InfisicalSubOrganization))]
|
||||
public sealed class NewInfisicalSubOrganizationCmdlet : InfisicalCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0)] public string Name { get; set; }
|
||||
[Parameter(Mandatory = true, Position = 1)] public string Slug { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ShouldProcess(Name, "Create Infisical sub-organization"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||
InfisicalSubOrganizationClient client = new InfisicalSubOrganizationClient(HttpClient, Logger);
|
||||
InfisicalSubOrganization subOrganization = client.Create(connection, Name, Slug);
|
||||
if (subOrganization != null)
|
||||
{
|
||||
WriteObject(subOrganization);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
ThrowTerminatingForException("NewInfisicalSubOrganizationCmdlet", "CreateSubOrganization", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSInfisicalAPI.Connections;
|
||||
using PSInfisicalAPI.Organizations;
|
||||
|
||||
namespace PSInfisicalAPI.Cmdlets
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Remove, "InfisicalOrganization", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
|
||||
public sealed class RemoveInfisicalOrganizationCmdlet : InfisicalCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
|
||||
[Alias("Id")]
|
||||
public string OrganizationId { get; set; }
|
||||
|
||||
[Parameter] public SwitchParameter PassThru { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
try
|
||||
{
|
||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||
|
||||
if (!ShouldProcess(OrganizationId, "Remove Infisical organization"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InfisicalOrganizationClient client = new InfisicalOrganizationClient(HttpClient, Logger);
|
||||
client.Delete(connection, OrganizationId);
|
||||
|
||||
if (PassThru.IsPresent)
|
||||
{
|
||||
WriteObject(OrganizationId);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
ThrowTerminatingForException("RemoveInfisicalOrganizationCmdlet", "DeleteOrganization", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSInfisicalAPI.Connections;
|
||||
using PSInfisicalAPI.SubOrganizations;
|
||||
|
||||
namespace PSInfisicalAPI.Cmdlets
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Remove, "InfisicalSubOrganization", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
|
||||
public sealed class RemoveInfisicalSubOrganizationCmdlet : InfisicalCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
|
||||
[Alias("Id")]
|
||||
public string SubOrganizationId { get; set; }
|
||||
|
||||
[Parameter] public SwitchParameter PassThru { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
try
|
||||
{
|
||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||
|
||||
if (!ShouldProcess(SubOrganizationId, "Remove Infisical sub-organization"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InfisicalSubOrganizationClient client = new InfisicalSubOrganizationClient(HttpClient, Logger);
|
||||
client.Delete(connection, SubOrganizationId);
|
||||
|
||||
if (PassThru.IsPresent)
|
||||
{
|
||||
WriteObject(SubOrganizationId);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
ThrowTerminatingForException("RemoveInfisicalSubOrganizationCmdlet", "DeleteSubOrganization", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Management.Automation;
|
||||
using System.Text.RegularExpressions;
|
||||
using PSInfisicalAPI.Models;
|
||||
using PSInfisicalAPI.Process;
|
||||
|
||||
namespace PSInfisicalAPI.Cmdlets
|
||||
{
|
||||
[Cmdlet(VerbsLifecycle.Start, "InfisicalProcess", DefaultParameterSetName = WindowStyleSet, SupportsShouldProcess = true)]
|
||||
[OutputType(typeof(InfisicalProcessResult))]
|
||||
public sealed class StartInfisicalProcessCmdlet : InfisicalCmdletBase
|
||||
{
|
||||
private const string Component = "StartInfisicalProcessCmdlet";
|
||||
private const string WindowStyleSet = "WindowStyle";
|
||||
private const string CreateNoWindowSet = "CreateNoWindow";
|
||||
|
||||
[Parameter(Mandatory = true, Position = 0)]
|
||||
[ValidateNotNullOrEmpty]
|
||||
[Alias("FP")]
|
||||
public string FilePath { get; set; }
|
||||
|
||||
[Parameter]
|
||||
[Alias("WD")]
|
||||
public DirectoryInfo WorkingDirectory { get; set; }
|
||||
|
||||
[Parameter]
|
||||
[AllowEmptyCollection]
|
||||
[AllowNull]
|
||||
[Alias("AL")]
|
||||
public string[] ArgumentList { get; set; }
|
||||
|
||||
[Parameter]
|
||||
[AllowEmptyCollection]
|
||||
[AllowNull]
|
||||
[Alias("AECL")]
|
||||
public string[] AcceptableExitCodeList { get; set; }
|
||||
|
||||
[Parameter(ParameterSetName = WindowStyleSet)]
|
||||
[ValidateSet("Normal", "Hidden", "Minimized", "Maximized")]
|
||||
[Alias("WS")]
|
||||
public string WindowStyle { get; set; } = "Hidden";
|
||||
|
||||
[Parameter(ParameterSetName = CreateNoWindowSet)]
|
||||
[Alias("CNW")]
|
||||
public SwitchParameter CreateNoWindow { get; set; }
|
||||
|
||||
[Parameter]
|
||||
[Alias("NW")]
|
||||
public SwitchParameter NoWait { get; set; }
|
||||
|
||||
[Parameter]
|
||||
[ValidateSet("AboveNormal", "BelowNormal", "High", "Idle", "Normal", "RealTime")]
|
||||
[Alias("P")]
|
||||
public string Priority { get; set; } = "Normal";
|
||||
|
||||
[Parameter]
|
||||
[Alias("ET")]
|
||||
public TimeSpan ExecutionTimeout { get; set; }
|
||||
|
||||
[Parameter]
|
||||
[Alias("ETI")]
|
||||
public TimeSpan ExecutionTimeoutInterval { get; set; } = TimeSpan.FromSeconds(15);
|
||||
|
||||
[Parameter]
|
||||
[Alias("SIO")]
|
||||
public object[] StandardInputObjectList { get; set; }
|
||||
|
||||
[Parameter]
|
||||
[Alias("ENV")]
|
||||
public IDictionary EnvironmentVariables { get; set; }
|
||||
|
||||
[Parameter]
|
||||
[Alias("StandardOutputParsingExpression", "SOPE", "PE")]
|
||||
public Regex ParsingExpression { get; set; }
|
||||
|
||||
[Parameter]
|
||||
[Alias("SAL")]
|
||||
public SwitchParameter SecureArgumentList { get; set; }
|
||||
|
||||
[Parameter]
|
||||
[Alias("LO")]
|
||||
public SwitchParameter LogOutput { get; set; }
|
||||
|
||||
[Parameter]
|
||||
[Alias("COE")]
|
||||
public SwitchParameter ContinueOnError { get; set; }
|
||||
|
||||
[Parameter(ValueFromPipeline = true)]
|
||||
[Alias("Secrets", "InputObject")]
|
||||
public InfisicalSecret[] Secret { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Prefix { get; set; }
|
||||
|
||||
private readonly List<InfisicalSecret> _secretBuffer = new List<InfisicalSecret>();
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (Secret == null) { return; }
|
||||
foreach (InfisicalSecret secret in Secret)
|
||||
{
|
||||
if (secret != null) { _secretBuffer.Add(secret); }
|
||||
}
|
||||
}
|
||||
|
||||
protected override void EndProcessing()
|
||||
{
|
||||
try
|
||||
{
|
||||
string target = string.IsNullOrEmpty(WorkingDirectory != null ? WorkingDirectory.FullName : null)
|
||||
? FilePath
|
||||
: string.Concat(FilePath, " (in ", WorkingDirectory.FullName, ")");
|
||||
|
||||
if (!ShouldProcess(target, "Start process with Infisical secrets")) { return; }
|
||||
|
||||
InfisicalProcessOptions options = new InfisicalProcessOptions
|
||||
{
|
||||
FilePath = FilePath,
|
||||
WorkingDirectory = WorkingDirectory,
|
||||
ArgumentList = ArgumentList,
|
||||
AcceptableExitCodeList = AcceptableExitCodeList,
|
||||
WindowStyle = WindowStyle,
|
||||
CreateNoWindow = CreateNoWindow.IsPresent,
|
||||
NoWait = NoWait.IsPresent,
|
||||
Priority = Priority,
|
||||
ExecutionTimeout = MyInvocation.BoundParameters.ContainsKey("ExecutionTimeout") ? (TimeSpan?)ExecutionTimeout : null,
|
||||
ExecutionTimeoutInterval = ExecutionTimeoutInterval,
|
||||
StandardInputObjectList = StandardInputObjectList,
|
||||
EnvironmentVariables = EnvironmentVariables,
|
||||
ParsingExpression = ParsingExpression,
|
||||
SecureArgumentList = SecureArgumentList.IsPresent,
|
||||
LogOutput = LogOutput.IsPresent,
|
||||
ContinueOnError = ContinueOnError.IsPresent,
|
||||
Secrets = _secretBuffer.ToArray(),
|
||||
Prefix = Prefix
|
||||
};
|
||||
|
||||
InfisicalProcessResult result = InfisicalProcessRunner.Run(options, Logger);
|
||||
WriteObject(result);
|
||||
|
||||
if (!result.Succeeded && !NoWait.IsPresent && !ContinueOnError.IsPresent)
|
||||
{
|
||||
string message = string.Concat("Process '", FilePath, "' exited with code ", result.ExitCode.HasValue ? result.ExitCode.Value.ToString() : "<null>", " which is not in the acceptable exit code list.");
|
||||
InvalidOperationException exception = new InvalidOperationException(message);
|
||||
ErrorRecord error = new ErrorRecord(exception, "StartInfisicalProcess.UnacceptableExitCode", ErrorCategory.InvalidResult, result);
|
||||
ThrowTerminatingError(error);
|
||||
}
|
||||
}
|
||||
catch (PipelineStoppedException) { throw; }
|
||||
catch (Exception exception)
|
||||
{
|
||||
ThrowTerminatingForException(Component, "StartProcess", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSInfisicalAPI.Connections;
|
||||
using PSInfisicalAPI.Models;
|
||||
using PSInfisicalAPI.Organizations;
|
||||
|
||||
namespace PSInfisicalAPI.Cmdlets
|
||||
{
|
||||
[Cmdlet(VerbsData.Update, "InfisicalOrganization", SupportsShouldProcess = true)]
|
||||
[OutputType(typeof(InfisicalOrganization))]
|
||||
public sealed class UpdateInfisicalOrganizationCmdlet : InfisicalCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
|
||||
[Alias("Id")]
|
||||
public string OrganizationId { get; set; }
|
||||
|
||||
[Parameter] public string Name { get; set; }
|
||||
[Parameter] public string Slug { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
try
|
||||
{
|
||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||
|
||||
if (!ShouldProcess(OrganizationId, "Update Infisical organization"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InfisicalOrganizationClient client = new InfisicalOrganizationClient(HttpClient, Logger);
|
||||
InfisicalOrganization organization = client.Update(connection, OrganizationId, Name, Slug);
|
||||
if (organization != null)
|
||||
{
|
||||
WriteObject(organization);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
ThrowTerminatingForException("UpdateInfisicalOrganizationCmdlet", "UpdateOrganization", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSInfisicalAPI.Connections;
|
||||
using PSInfisicalAPI.Models;
|
||||
using PSInfisicalAPI.SubOrganizations;
|
||||
|
||||
namespace PSInfisicalAPI.Cmdlets
|
||||
{
|
||||
[Cmdlet(VerbsData.Update, "InfisicalSubOrganization", SupportsShouldProcess = true)]
|
||||
[OutputType(typeof(InfisicalSubOrganization))]
|
||||
public sealed class UpdateInfisicalSubOrganizationCmdlet : InfisicalCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
|
||||
[Alias("Id")]
|
||||
public string SubOrganizationId { get; set; }
|
||||
|
||||
[Parameter] public string Name { get; set; }
|
||||
[Parameter] public string Slug { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
try
|
||||
{
|
||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||
|
||||
if (!ShouldProcess(SubOrganizationId, "Update Infisical sub-organization"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InfisicalSubOrganizationClient client = new InfisicalSubOrganizationClient(HttpClient, Logger);
|
||||
InfisicalSubOrganization subOrganization = client.Update(connection, SubOrganizationId, Name, Slug);
|
||||
if (subOrganization != null)
|
||||
{
|
||||
WriteObject(subOrganization);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
ThrowTerminatingForException("UpdateInfisicalSubOrganizationCmdlet", "UpdateSubOrganization", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@ namespace PSInfisicalAPI.Connections
|
||||
public DateTimeOffset ConnectedAtUtc { get; set; }
|
||||
public DateTimeOffset? ExpiresAtUtc { get; set; }
|
||||
public bool IsConnected { get; set; }
|
||||
public bool SkipCertificateCheck { get; set; }
|
||||
public bool AllowInsecureTransport { get; set; }
|
||||
|
||||
public Dictionary<string, string> ResolvedEndpointVersions { get; } = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
|
||||
|
||||
@@ -44,6 +44,18 @@ namespace PSInfisicalAPI.Endpoints
|
||||
public const string UpdateTag = "UpdateTag";
|
||||
public const string DeleteTag = "DeleteTag";
|
||||
|
||||
public const string ListOrganizations = "ListOrganizations";
|
||||
public const string RetrieveOrganization = "RetrieveOrganization";
|
||||
public const string CreateOrganization = "CreateOrganization";
|
||||
public const string UpdateOrganization = "UpdateOrganization";
|
||||
public const string DeleteOrganization = "DeleteOrganization";
|
||||
|
||||
public const string ListSubOrganizations = "ListSubOrganizations";
|
||||
public const string RetrieveSubOrganization = "RetrieveSubOrganization";
|
||||
public const string CreateSubOrganization = "CreateSubOrganization";
|
||||
public const string UpdateSubOrganization = "UpdateSubOrganization";
|
||||
public const string DeleteSubOrganization = "DeleteSubOrganization";
|
||||
|
||||
public const string ListInternalCertificateAuthorities = "ListInternalCertificateAuthorities";
|
||||
public const string RetrieveInternalCertificateAuthority = "RetrieveInternalCertificateAuthority";
|
||||
public const string SearchCertificates = "SearchCertificates";
|
||||
|
||||
@@ -16,6 +16,8 @@ namespace PSInfisicalAPI.Endpoints
|
||||
RegisterEnvironments(Candidates);
|
||||
RegisterFolders(Candidates);
|
||||
RegisterTags(Candidates);
|
||||
RegisterOrganizations(Candidates);
|
||||
RegisterSubOrganizations(Candidates);
|
||||
RegisterPki(Candidates);
|
||||
}
|
||||
|
||||
@@ -496,6 +498,112 @@ namespace PSInfisicalAPI.Endpoints
|
||||
});
|
||||
}
|
||||
|
||||
private static void RegisterOrganizations(Dictionary<string, List<InfisicalEndpointDefinition>> map)
|
||||
{
|
||||
Add(map, new InfisicalEndpointDefinition
|
||||
{
|
||||
Name = InfisicalEndpointNames.ListOrganizations,
|
||||
Resource = "Organizations",
|
||||
Version = "v2",
|
||||
Method = "GET",
|
||||
Template = "/api/v2/organizations",
|
||||
RequiresAuthorization = true
|
||||
});
|
||||
|
||||
Add(map, new InfisicalEndpointDefinition
|
||||
{
|
||||
Name = InfisicalEndpointNames.RetrieveOrganization,
|
||||
Resource = "Organizations",
|
||||
Version = "v1",
|
||||
Method = "GET",
|
||||
Template = "/api/v1/organization/{organizationId}",
|
||||
RequiresAuthorization = true
|
||||
});
|
||||
|
||||
Add(map, new InfisicalEndpointDefinition
|
||||
{
|
||||
Name = InfisicalEndpointNames.CreateOrganization,
|
||||
Resource = "Organizations",
|
||||
Version = "v2",
|
||||
Method = "POST",
|
||||
Template = "/api/v2/organizations",
|
||||
RequiresAuthorization = true
|
||||
});
|
||||
|
||||
Add(map, new InfisicalEndpointDefinition
|
||||
{
|
||||
Name = InfisicalEndpointNames.UpdateOrganization,
|
||||
Resource = "Organizations",
|
||||
Version = "v1",
|
||||
Method = "PATCH",
|
||||
Template = "/api/v1/organization/{organizationId}",
|
||||
RequiresAuthorization = true
|
||||
});
|
||||
|
||||
Add(map, new InfisicalEndpointDefinition
|
||||
{
|
||||
Name = InfisicalEndpointNames.DeleteOrganization,
|
||||
Resource = "Organizations",
|
||||
Version = "v1",
|
||||
Method = "DELETE",
|
||||
Template = "/api/v1/organization/{organizationId}",
|
||||
RequiresAuthorization = true
|
||||
});
|
||||
}
|
||||
|
||||
private static void RegisterSubOrganizations(Dictionary<string, List<InfisicalEndpointDefinition>> map)
|
||||
{
|
||||
Add(map, new InfisicalEndpointDefinition
|
||||
{
|
||||
Name = InfisicalEndpointNames.ListSubOrganizations,
|
||||
Resource = "SubOrganizations",
|
||||
Version = "v1",
|
||||
Method = "GET",
|
||||
Template = "/api/v1/sub-organizations",
|
||||
RequiresAuthorization = true
|
||||
});
|
||||
|
||||
Add(map, new InfisicalEndpointDefinition
|
||||
{
|
||||
Name = InfisicalEndpointNames.RetrieveSubOrganization,
|
||||
Resource = "SubOrganizations",
|
||||
Version = "v1",
|
||||
Method = "GET",
|
||||
Template = "/api/v1/sub-organizations/{subOrgId}",
|
||||
RequiresAuthorization = true
|
||||
});
|
||||
|
||||
Add(map, new InfisicalEndpointDefinition
|
||||
{
|
||||
Name = InfisicalEndpointNames.CreateSubOrganization,
|
||||
Resource = "SubOrganizations",
|
||||
Version = "v1",
|
||||
Method = "POST",
|
||||
Template = "/api/v1/sub-organizations",
|
||||
RequiresAuthorization = true
|
||||
});
|
||||
|
||||
Add(map, new InfisicalEndpointDefinition
|
||||
{
|
||||
Name = InfisicalEndpointNames.UpdateSubOrganization,
|
||||
Resource = "SubOrganizations",
|
||||
Version = "v1",
|
||||
Method = "PATCH",
|
||||
Template = "/api/v1/sub-organizations/{subOrgId}",
|
||||
RequiresAuthorization = true
|
||||
});
|
||||
|
||||
Add(map, new InfisicalEndpointDefinition
|
||||
{
|
||||
Name = InfisicalEndpointNames.DeleteSubOrganization,
|
||||
Resource = "SubOrganizations",
|
||||
Version = "v1",
|
||||
Method = "DELETE",
|
||||
Template = "/api/v1/sub-organizations/{subOrgId}",
|
||||
RequiresAuthorization = true
|
||||
});
|
||||
}
|
||||
|
||||
private static void RegisterPki(Dictionary<string, List<InfisicalEndpointDefinition>> map)
|
||||
{
|
||||
Add(map, new InfisicalEndpointDefinition
|
||||
|
||||
@@ -2,6 +2,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Security;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using PSInfisicalAPI.Errors;
|
||||
using PSInfisicalAPI.Logging;
|
||||
@@ -11,13 +13,18 @@ namespace PSInfisicalAPI.Http
|
||||
public sealed class InfisicalHttpClient : IInfisicalHttpClient
|
||||
{
|
||||
private const string Component = "HttpClient";
|
||||
private static readonly PropertyInfo PerRequestCertCallbackProperty =
|
||||
typeof(HttpWebRequest).GetProperty("ServerCertificateValidationCallback");
|
||||
|
||||
private readonly IInfisicalLogger _logger;
|
||||
private readonly int _timeoutSeconds;
|
||||
private readonly bool _skipCertificateCheck;
|
||||
|
||||
public InfisicalHttpClient(IInfisicalLogger logger, int timeoutSeconds = 100)
|
||||
public InfisicalHttpClient(IInfisicalLogger logger, int timeoutSeconds = 100, bool skipCertificateCheck = false)
|
||||
{
|
||||
_logger = logger ?? NullInfisicalLogger.Instance;
|
||||
_timeoutSeconds = timeoutSeconds;
|
||||
_skipCertificateCheck = skipCertificateCheck;
|
||||
}
|
||||
|
||||
public InfisicalHttpResponse Send(InfisicalHttpRequest request)
|
||||
@@ -44,6 +51,11 @@ namespace PSInfisicalAPI.Http
|
||||
webRequest.ReadWriteTimeout = _timeoutSeconds * 1000;
|
||||
webRequest.UseDefaultCredentials = true;
|
||||
|
||||
if (_skipCertificateCheck)
|
||||
{
|
||||
ApplyInsecureCertificateBypass(webRequest);
|
||||
}
|
||||
|
||||
IWebProxy systemProxy = WebRequest.GetSystemWebProxy();
|
||||
if (systemProxy != null)
|
||||
{
|
||||
@@ -95,6 +107,20 @@ namespace PSInfisicalAPI.Http
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyInsecureCertificateBypass(HttpWebRequest webRequest)
|
||||
{
|
||||
RemoteCertificateValidationCallback callback = (sender, certificate, chain, errors) => true;
|
||||
|
||||
if (PerRequestCertCallbackProperty != null && PerRequestCertCallbackProperty.CanWrite)
|
||||
{
|
||||
PerRequestCertCallbackProperty.SetValue(webRequest, callback, null);
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.Warning(Component, "Per-request ServerCertificateValidationCallback unavailable on this runtime; falling back to global ServicePointManager override for this process.");
|
||||
ServicePointManager.ServerCertificateValidationCallback = callback;
|
||||
}
|
||||
|
||||
private static void ApplyHeaders(HttpWebRequest webRequest, IDictionary<string, string> headers)
|
||||
{
|
||||
if (headers == null)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
|
||||
namespace PSInfisicalAPI.Models
|
||||
{
|
||||
public sealed class InfisicalOrganization
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public string CustomerId { get; set; }
|
||||
public bool AuthEnforced { get; set; }
|
||||
public bool ScimEnabled { get; set; }
|
||||
public DateTimeOffset? CreatedAtUtc { get; set; }
|
||||
public DateTimeOffset? UpdatedAtUtc { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.IsNullOrEmpty(Slug) ? (Name ?? Id) : Slug;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace PSInfisicalAPI.Models
|
||||
{
|
||||
public sealed class InfisicalProcessResult
|
||||
{
|
||||
public string FilePath { get; set; }
|
||||
public int? ProcessId { get; set; }
|
||||
public int? ExitCode { get; set; }
|
||||
public string ExitCodeAsHex { get; set; }
|
||||
public int? ExitCodeAsInteger { get; set; }
|
||||
public string ExitCodeAsDecimal { get; set; }
|
||||
public DateTime? StartTime { get; set; }
|
||||
public DateTime? ExitTime { get; set; }
|
||||
public TimeSpan? Duration { get; set; }
|
||||
public string DurationFriendly { get; set; }
|
||||
public string StandardOutput { get; set; }
|
||||
public string StandardError { get; set; }
|
||||
public Match[] StandardOutputObject { get; set; }
|
||||
public Match[] StandardErrorObject { get; set; }
|
||||
public bool TimedOut { get; set; }
|
||||
public bool Succeeded { get; set; }
|
||||
public int SecretCount { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace PSInfisicalAPI.Models
|
||||
{
|
||||
public sealed class InfisicalSubOrganization
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public string OrganizationId { get; set; }
|
||||
public bool IsAccessible { get; set; }
|
||||
public DateTimeOffset? CreatedAtUtc { get; set; }
|
||||
public DateTimeOffset? UpdatedAtUtc { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.IsNullOrEmpty(Slug) ? (Name ?? Id) : Slug;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using PSInfisicalAPI.Connections;
|
||||
using PSInfisicalAPI.Endpoints;
|
||||
using PSInfisicalAPI.Errors;
|
||||
using PSInfisicalAPI.Http;
|
||||
using PSInfisicalAPI.Logging;
|
||||
using PSInfisicalAPI.Models;
|
||||
using PSInfisicalAPI.Serialization;
|
||||
|
||||
namespace PSInfisicalAPI.Organizations
|
||||
{
|
||||
public sealed class InfisicalOrganizationClient
|
||||
{
|
||||
private const string Component = "OrganizationClient";
|
||||
|
||||
private readonly IInfisicalLogger _logger;
|
||||
private readonly JsonInfisicalSerializer _serializer;
|
||||
private readonly InfisicalApiInvoker _invoker;
|
||||
|
||||
public InfisicalOrganizationClient(IInfisicalHttpClient httpClient, IInfisicalLogger logger)
|
||||
{
|
||||
if (httpClient == null) { throw new ArgumentNullException(nameof(httpClient)); }
|
||||
_logger = logger ?? NullInfisicalLogger.Instance;
|
||||
_serializer = new JsonInfisicalSerializer();
|
||||
_invoker = new InfisicalApiInvoker(httpClient);
|
||||
}
|
||||
|
||||
public InfisicalOrganization[] List(InfisicalConnection connection)
|
||||
{
|
||||
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Information(Component, "Attempting to list Infisical organizations. Please Wait...");
|
||||
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.ListOrganizations, "ListOrganizations", null, null, null);
|
||||
InfisicalOrganizationListResponseDto dto = _serializer.Deserialize<InfisicalOrganizationListResponseDto>(response.Body);
|
||||
response.Clear();
|
||||
|
||||
InfisicalOrganization[] mapped = InfisicalOrganizationMapper.MapMany(dto != null ? dto.Organizations : null);
|
||||
_logger.Information(Component, "Infisical organization list retrieval was successful.");
|
||||
return mapped;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_logger.Error(Component, "Infisical organization list retrieval failed.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public InfisicalOrganization Retrieve(InfisicalConnection connection, string organizationId)
|
||||
{
|
||||
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||
if (string.IsNullOrEmpty(organizationId)) { throw new InfisicalConfigurationException("OrganizationId is required."); }
|
||||
|
||||
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "organizationId", organizationId } };
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Information(Component, string.Concat("Attempting to retrieve Infisical organization '", organizationId, "'. Please Wait..."));
|
||||
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.RetrieveOrganization, "RetrieveOrganization", pathParameters, null, null);
|
||||
InfisicalOrganizationSingleResponseDto dto = _serializer.Deserialize<InfisicalOrganizationSingleResponseDto>(response.Body);
|
||||
response.Clear();
|
||||
|
||||
InfisicalOrganization mapped = InfisicalOrganizationMapper.Map(dto != null ? dto.Organization : null);
|
||||
_logger.Information(Component, "Infisical organization retrieval was successful.");
|
||||
return mapped;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_logger.Error(Component, "Infisical organization retrieval failed.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public InfisicalOrganization Create(InfisicalConnection connection, string name, string slug)
|
||||
{
|
||||
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||
if (string.IsNullOrEmpty(name)) { throw new InfisicalConfigurationException("Name is required."); }
|
||||
|
||||
InfisicalOrganizationCreateRequestDto request = new InfisicalOrganizationCreateRequestDto { Name = name, Slug = slug };
|
||||
string body = _serializer.Serialize(request);
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Information(Component, string.Concat("Attempting to create Infisical organization '", name, "'. Please Wait..."));
|
||||
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.CreateOrganization, "CreateOrganization", null, null, body);
|
||||
InfisicalOrganizationSingleResponseDto dto = _serializer.Deserialize<InfisicalOrganizationSingleResponseDto>(response.Body);
|
||||
response.Clear();
|
||||
|
||||
InfisicalOrganization mapped = InfisicalOrganizationMapper.Map(dto != null ? dto.Organization : null);
|
||||
_logger.Information(Component, "Infisical organization creation was successful.");
|
||||
return mapped;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_logger.Error(Component, "Infisical organization creation failed.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public InfisicalOrganization Update(InfisicalConnection connection, string organizationId, string name, string slug)
|
||||
{
|
||||
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||
if (string.IsNullOrEmpty(organizationId)) { throw new InfisicalConfigurationException("OrganizationId is required."); }
|
||||
|
||||
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "organizationId", organizationId } };
|
||||
InfisicalOrganizationUpdateRequestDto request = new InfisicalOrganizationUpdateRequestDto { Name = name, Slug = slug };
|
||||
string body = _serializer.Serialize(request);
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Information(Component, string.Concat("Attempting to update Infisical organization '", organizationId, "'. Please Wait..."));
|
||||
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.UpdateOrganization, "UpdateOrganization", pathParameters, null, body);
|
||||
InfisicalOrganizationSingleResponseDto dto = _serializer.Deserialize<InfisicalOrganizationSingleResponseDto>(response.Body);
|
||||
response.Clear();
|
||||
|
||||
InfisicalOrganization mapped = InfisicalOrganizationMapper.Map(dto != null ? dto.Organization : null);
|
||||
_logger.Information(Component, "Infisical organization update was successful.");
|
||||
return mapped;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_logger.Error(Component, "Infisical organization update failed.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void Delete(InfisicalConnection connection, string organizationId)
|
||||
{
|
||||
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||
if (string.IsNullOrEmpty(organizationId)) { throw new InfisicalConfigurationException("OrganizationId is required."); }
|
||||
|
||||
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "organizationId", organizationId } };
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Information(Component, string.Concat("Attempting to delete Infisical organization '", organizationId, "'. Please Wait..."));
|
||||
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.DeleteOrganization, "DeleteOrganization", pathParameters, null, null);
|
||||
response.Clear();
|
||||
_logger.Information(Component, "Infisical organization deletion was successful.");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_logger.Error(Component, "Infisical organization deletion failed.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PSInfisicalAPI.Organizations
|
||||
{
|
||||
internal sealed class InfisicalOrganizationResponseDto
|
||||
{
|
||||
[JsonProperty("id")] public string Id { get; set; }
|
||||
[JsonProperty("_id")] public string InternalId { get; set; }
|
||||
[JsonProperty("name")] public string Name { get; set; }
|
||||
[JsonProperty("slug")] public string Slug { get; set; }
|
||||
[JsonProperty("customerId")] public string CustomerId { get; set; }
|
||||
[JsonProperty("authEnforced")] public bool AuthEnforced { get; set; }
|
||||
[JsonProperty("scimEnabled")] public bool ScimEnabled { get; set; }
|
||||
[JsonProperty("createdAt")] public string CreatedAt { get; set; }
|
||||
[JsonProperty("updatedAt")] public string UpdatedAt { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class InfisicalOrganizationListResponseDto
|
||||
{
|
||||
[JsonProperty("organizations")] public List<InfisicalOrganizationResponseDto> Organizations { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class InfisicalOrganizationSingleResponseDto
|
||||
{
|
||||
[JsonProperty("organization")] public InfisicalOrganizationResponseDto Organization { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class InfisicalOrganizationCreateRequestDto
|
||||
{
|
||||
[JsonProperty("name")] public string Name { get; set; }
|
||||
[JsonProperty("slug", NullValueHandling = NullValueHandling.Ignore)] public string Slug { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class InfisicalOrganizationUpdateRequestDto
|
||||
{
|
||||
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] public string Name { get; set; }
|
||||
[JsonProperty("slug", NullValueHandling = NullValueHandling.Ignore)] public string Slug { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using PSInfisicalAPI.Models;
|
||||
|
||||
namespace PSInfisicalAPI.Organizations
|
||||
{
|
||||
internal static class InfisicalOrganizationMapper
|
||||
{
|
||||
public static InfisicalOrganization Map(InfisicalOrganizationResponseDto dto)
|
||||
{
|
||||
if (dto == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new InfisicalOrganization
|
||||
{
|
||||
Id = !string.IsNullOrEmpty(dto.Id) ? dto.Id : dto.InternalId,
|
||||
Name = dto.Name,
|
||||
Slug = dto.Slug,
|
||||
CustomerId = dto.CustomerId,
|
||||
AuthEnforced = dto.AuthEnforced,
|
||||
ScimEnabled = dto.ScimEnabled,
|
||||
CreatedAtUtc = ParseTimestamp(dto.CreatedAt),
|
||||
UpdatedAtUtc = ParseTimestamp(dto.UpdatedAt)
|
||||
};
|
||||
}
|
||||
|
||||
public static InfisicalOrganization[] MapMany(IEnumerable<InfisicalOrganizationResponseDto> items)
|
||||
{
|
||||
if (items == null)
|
||||
{
|
||||
return Array.Empty<InfisicalOrganization>();
|
||||
}
|
||||
|
||||
List<InfisicalOrganization> results = new List<InfisicalOrganization>();
|
||||
foreach (InfisicalOrganizationResponseDto dto in items)
|
||||
{
|
||||
InfisicalOrganization mapped = Map(dto);
|
||||
if (mapped != null)
|
||||
{
|
||||
results.Add(mapped);
|
||||
}
|
||||
}
|
||||
|
||||
return results.ToArray();
|
||||
}
|
||||
|
||||
private static DateTimeOffset? ParseTimestamp(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
DateTimeOffset parsed;
|
||||
if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out parsed))
|
||||
{
|
||||
return parsed;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using PSInfisicalAPI.Models;
|
||||
|
||||
namespace PSInfisicalAPI.Process
|
||||
{
|
||||
public sealed class InfisicalProcessOptions
|
||||
{
|
||||
public string FilePath { get; set; }
|
||||
public DirectoryInfo WorkingDirectory { get; set; }
|
||||
public string[] ArgumentList { get; set; }
|
||||
public string[] AcceptableExitCodeList { get; set; }
|
||||
public string WindowStyle { get; set; }
|
||||
public bool CreateNoWindow { get; set; }
|
||||
public bool NoWait { get; set; }
|
||||
public string Priority { get; set; }
|
||||
public TimeSpan? ExecutionTimeout { get; set; }
|
||||
public TimeSpan ExecutionTimeoutInterval { get; set; }
|
||||
public object[] StandardInputObjectList { get; set; }
|
||||
public IDictionary EnvironmentVariables { get; set; }
|
||||
public Regex ParsingExpression { get; set; }
|
||||
public bool SecureArgumentList { get; set; }
|
||||
public bool LogOutput { get; set; }
|
||||
public bool ContinueOnError { get; set; }
|
||||
public InfisicalSecret[] Secrets { get; set; }
|
||||
public string Prefix { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using PSInfisicalAPI.Logging;
|
||||
using PSInfisicalAPI.Models;
|
||||
using SystemProcess = System.Diagnostics.Process;
|
||||
using static PSInfisicalAPI.Process.InfisicalProcessRunnerHelpers;
|
||||
|
||||
namespace PSInfisicalAPI.Process
|
||||
{
|
||||
public static class InfisicalProcessRunner
|
||||
{
|
||||
private const string Component = "InfisicalProcessRunner";
|
||||
|
||||
public static InfisicalProcessResult Run(InfisicalProcessOptions options, IInfisicalLogger logger)
|
||||
{
|
||||
if (options == null) { throw new ArgumentNullException(nameof(options)); }
|
||||
if (string.IsNullOrWhiteSpace(options.FilePath)) { throw new ArgumentException("FilePath is required.", nameof(options)); }
|
||||
|
||||
string[] acceptable = (options.AcceptableExitCodeList != null && options.AcceptableExitCodeList.Length > 0)
|
||||
? options.AcceptableExitCodeList
|
||||
: new[] { "0", "3010" };
|
||||
|
||||
InfisicalProcessResult result = new InfisicalProcessResult
|
||||
{
|
||||
FilePath = options.FilePath,
|
||||
ExitCode = -1,
|
||||
SecretCount = options.Secrets != null ? options.Secrets.Length : 0
|
||||
};
|
||||
|
||||
SystemProcess process = new SystemProcess();
|
||||
process.StartInfo.FileName = options.FilePath;
|
||||
process.StartInfo.UseShellExecute = false;
|
||||
process.StartInfo.RedirectStandardOutput = true;
|
||||
process.StartInfo.RedirectStandardError = true;
|
||||
process.StartInfo.RedirectStandardInput = true;
|
||||
|
||||
if (options.CreateNoWindow) { process.StartInfo.CreateNoWindow = true; }
|
||||
else if (!string.IsNullOrWhiteSpace(options.WindowStyle))
|
||||
{
|
||||
process.StartInfo.WindowStyle = (ProcessWindowStyle)Enum.Parse(typeof(ProcessWindowStyle), options.WindowStyle, true);
|
||||
}
|
||||
|
||||
if (options.WorkingDirectory != null && !string.IsNullOrWhiteSpace(options.WorkingDirectory.FullName))
|
||||
{
|
||||
if (!Directory.Exists(options.WorkingDirectory.FullName))
|
||||
{
|
||||
Directory.CreateDirectory(options.WorkingDirectory.FullName);
|
||||
}
|
||||
process.StartInfo.WorkingDirectory = options.WorkingDirectory.FullName;
|
||||
}
|
||||
|
||||
ApplyEnvironment(process.StartInfo.Environment, options, logger);
|
||||
|
||||
if (options.ArgumentList != null && options.ArgumentList.Length > 0)
|
||||
{
|
||||
process.StartInfo.Arguments = string.Join(" ", options.ArgumentList);
|
||||
}
|
||||
|
||||
LogCommand(process.StartInfo, options, logger);
|
||||
LogVerbose(logger, string.Concat("Acceptable exit codes: ", string.Join("; ", acceptable)));
|
||||
|
||||
Stopwatch timer = Stopwatch.StartNew();
|
||||
process.Start();
|
||||
result.ProcessId = process.Id;
|
||||
try { result.StartTime = process.StartTime; } catch { }
|
||||
|
||||
ApplyPriority(process, options.Priority, logger);
|
||||
|
||||
StringBuilder stdoutBuffer = new StringBuilder();
|
||||
StringBuilder stderrBuffer = new StringBuilder();
|
||||
object stdoutGate = new object();
|
||||
object stderrGate = new object();
|
||||
process.OutputDataReceived += (sender, args) => { if (args.Data != null) { lock (stdoutGate) { stdoutBuffer.AppendLine(args.Data); } } };
|
||||
process.ErrorDataReceived += (sender, args) => { if (args.Data != null) { lock (stderrGate) { stderrBuffer.AppendLine(args.Data); } } };
|
||||
process.BeginOutputReadLine();
|
||||
process.BeginErrorReadLine();
|
||||
|
||||
WriteStandardInput(process, options.StandardInputObjectList, options.SecureArgumentList, logger);
|
||||
|
||||
if (options.NoWait)
|
||||
{
|
||||
LogVerbose(logger, string.Concat("Skipping wait for process ID ", process.Id, "."));
|
||||
return result;
|
||||
}
|
||||
|
||||
WaitForExit(process, options.ExecutionTimeout, options.ExecutionTimeoutInterval, timer, result, logger);
|
||||
|
||||
try { process.WaitForExit(); } catch { }
|
||||
lock (stdoutGate) { result.StandardOutput = stdoutBuffer.Length > 0 ? stdoutBuffer.ToString() : null; }
|
||||
lock (stderrGate) { result.StandardError = stderrBuffer.Length > 0 ? stderrBuffer.ToString() : null; }
|
||||
|
||||
try { result.ExitCode = process.ExitCode; } catch { result.ExitCode = null; }
|
||||
FormatExitCodes(result);
|
||||
try { result.ExitTime = process.ExitTime; } catch { }
|
||||
if (result.StartTime.HasValue && result.ExitTime.HasValue) { result.Duration = result.ExitTime.Value - result.StartTime.Value; }
|
||||
if (result.Duration.HasValue)
|
||||
{
|
||||
result.DurationFriendly = FormatFriendly(result.Duration.Value);
|
||||
LogVerbose(logger, string.Concat("The command execution took ", result.DurationFriendly, "."));
|
||||
}
|
||||
|
||||
ApplyRegex(result, options.ParsingExpression);
|
||||
result.Succeeded = IsAcceptable(result, acceptable);
|
||||
|
||||
try { process.Dispose(); } catch { }
|
||||
|
||||
if (options.LogOutput || !result.Succeeded)
|
||||
{
|
||||
LogVerbose(logger, string.Concat("StandardOutput: ", string.IsNullOrEmpty(result.StandardOutput) ? "N/A" : result.StandardOutput));
|
||||
LogVerbose(logger, string.Concat("StandardError: ", string.IsNullOrEmpty(result.StandardError) ? "N/A" : result.StandardError));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool IsAcceptable(InfisicalProcessResult result, string[] acceptable)
|
||||
{
|
||||
if (acceptable.Any(c => string.Equals(c, "*", StringComparison.Ordinal))) { return true; }
|
||||
HashSet<string> set = new HashSet<string>(acceptable, StringComparer.OrdinalIgnoreCase);
|
||||
if (result.ExitCode.HasValue && set.Contains(result.ExitCode.Value.ToString(CultureInfo.InvariantCulture))) { return true; }
|
||||
if (!string.IsNullOrEmpty(result.ExitCodeAsHex) && set.Contains(result.ExitCodeAsHex)) { return true; }
|
||||
if (result.ExitCodeAsInteger.HasValue && set.Contains(result.ExitCodeAsInteger.Value.ToString(CultureInfo.InvariantCulture))) { return true; }
|
||||
if (!string.IsNullOrEmpty(result.ExitCodeAsDecimal) && set.Contains(result.ExitCodeAsDecimal)) { return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void FormatExitCodes(InfisicalProcessResult result)
|
||||
{
|
||||
if (!result.ExitCode.HasValue) { return; }
|
||||
try
|
||||
{
|
||||
result.ExitCodeAsHex = "0x" + Convert.ToString(result.ExitCode.Value, 16).PadLeft(8, '0').ToUpperInvariant();
|
||||
int parsed;
|
||||
if (int.TryParse(result.ExitCodeAsHex.Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out parsed))
|
||||
{
|
||||
result.ExitCodeAsInteger = parsed;
|
||||
}
|
||||
result.ExitCodeAsDecimal = result.ExitCode.Value.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private static void LogVerbose(IInfisicalLogger logger, string message)
|
||||
{
|
||||
if (logger != null) { logger.Verbose(Component, message); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using PSInfisicalAPI.Logging;
|
||||
using PSInfisicalAPI.Models;
|
||||
using PSInfisicalAPI.Security;
|
||||
using SystemProcess = System.Diagnostics.Process;
|
||||
|
||||
namespace PSInfisicalAPI.Process
|
||||
{
|
||||
internal static class InfisicalProcessRunnerHelpers
|
||||
{
|
||||
private const string Component = "InfisicalProcessRunner";
|
||||
|
||||
internal static void ApplyEnvironment(IDictionary<string, string> processEnv, InfisicalProcessOptions options, IInfisicalLogger logger)
|
||||
{
|
||||
if (processEnv == null) { return; }
|
||||
|
||||
if (options.EnvironmentVariables != null && options.EnvironmentVariables.Count > 0)
|
||||
{
|
||||
Log(logger, string.Concat("Injecting ", options.EnvironmentVariables.Count, " explicit environment variable(s) into the process."));
|
||||
foreach (DictionaryEntry entry in options.EnvironmentVariables)
|
||||
{
|
||||
if (entry.Key == null) { continue; }
|
||||
string key = entry.Key.ToString();
|
||||
string value = entry.Value != null ? entry.Value.ToString() : string.Empty;
|
||||
processEnv[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.Secrets == null || options.Secrets.Length == 0) { return; }
|
||||
|
||||
Log(logger, string.Concat("Injecting ", options.Secrets.Length, " Infisical secret(s) into the process environment."));
|
||||
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);
|
||||
SecureStringUtility.UsePlainText(secret.SecretValue, plain =>
|
||||
{
|
||||
processEnv[name] = plain;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal static void LogCommand(ProcessStartInfo startInfo, InfisicalProcessOptions options, IInfisicalLogger logger)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.Append("Attempting to execute: ");
|
||||
builder.Append(startInfo.FileName);
|
||||
|
||||
if (!string.IsNullOrEmpty(startInfo.Arguments))
|
||||
{
|
||||
builder.Append(' ');
|
||||
if (options.SecureArgumentList)
|
||||
{
|
||||
int len = Math.Min(20, Math.Max(5, startInfo.Arguments.Length));
|
||||
builder.Append(new string('*', len));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append(startInfo.Arguments);
|
||||
}
|
||||
}
|
||||
|
||||
Log(logger, builder.ToString());
|
||||
}
|
||||
|
||||
internal static void ApplyPriority(SystemProcess process, string priority, IInfisicalLogger logger)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(priority)) { return; }
|
||||
ProcessPriorityClass desired;
|
||||
if (!Enum.TryParse(priority, true, out desired)) { return; }
|
||||
try
|
||||
{
|
||||
if (process.PriorityClass != desired)
|
||||
{
|
||||
process.PriorityClass = desired;
|
||||
Log(logger, string.Concat("Set process priority class to '", desired, "' for process ID ", process.Id, "."));
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log(logger, string.Concat("Unable to set process priority class to '", desired, "': ", exception.Message));
|
||||
}
|
||||
}
|
||||
|
||||
internal static void WriteStandardInput(SystemProcess process, object[] inputs, bool secureArguments, IInfisicalLogger logger)
|
||||
{
|
||||
if (inputs == null || inputs.Length == 0) { return; }
|
||||
for (int i = 0; i < inputs.Length; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
object value = inputs[i];
|
||||
string preview = secureArguments ? new string('*', 8) : (value != null ? value.ToString() : string.Empty);
|
||||
Log(logger, string.Concat("Writing standard input object ", i + 1, " of ", inputs.Length, " to process ID ", process.Id, ": ", preview));
|
||||
process.StandardInput.WriteLine(value);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log(logger, string.Concat("Failed to write standard input object ", i + 1, ": ", exception.Message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void WaitForExit(SystemProcess process, TimeSpan? timeout, TimeSpan interval, Stopwatch timer, InfisicalProcessResult result, IInfisicalLogger logger)
|
||||
{
|
||||
TimeSpan pollInterval = interval.TotalMilliseconds > 0 ? interval : TimeSpan.FromSeconds(15);
|
||||
int processId = process.Id;
|
||||
|
||||
if (!timeout.HasValue)
|
||||
{
|
||||
Log(logger, string.Concat("A timeout was not specified for process ID ", processId, "."));
|
||||
Log(logger, string.Concat("The wait for process ID ", processId, " termination will be indefinite."));
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(logger, string.Concat("Process timeout duration: ", FormatFriendly(timeout.Value)));
|
||||
}
|
||||
|
||||
while (!GetProcessHasExited(processId))
|
||||
{
|
||||
Log(logger, string.Concat("Process ID ", processId, " has been running for ", FormatFriendly(timer.Elapsed), "."));
|
||||
|
||||
if (timeout.HasValue && timer.Elapsed >= timeout.Value)
|
||||
{
|
||||
Log(logger, string.Concat("Process ID ", processId, " exceeded the maximum timeout duration of ", FormatFriendly(timeout.Value), "; terminating."));
|
||||
try { process.Kill(); result.TimedOut = true; } catch { }
|
||||
break;
|
||||
}
|
||||
|
||||
Log(logger, string.Concat("Checking again in another ", FormatFriendly(pollInterval), ". Please wait..."));
|
||||
System.Threading.Thread.Sleep(pollInterval);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool GetProcessHasExited(int processId)
|
||||
{
|
||||
try { return SystemProcess.GetProcessById(processId).HasExited; }
|
||||
catch { return true; }
|
||||
}
|
||||
|
||||
internal static string FormatFriendly(TimeSpan value)
|
||||
{
|
||||
string[] names = new[] { "Days", "Hours", "Minutes", "Seconds", "Milliseconds" };
|
||||
int[] values = new[] { value.Days, value.Hours, value.Minutes, value.Seconds, value.Milliseconds };
|
||||
|
||||
List<int> nonZeroIndices = new List<int>();
|
||||
for (int i = 0; i < values.Length; i++) { if (values[i] > 0) { nonZeroIndices.Add(i); } }
|
||||
|
||||
if (nonZeroIndices.Count == 0) { return "N/A"; }
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int last = nonZeroIndices.Count - 1;
|
||||
for (int i = 0; i < nonZeroIndices.Count; i++)
|
||||
{
|
||||
int index = nonZeroIndices[i];
|
||||
int amount = values[index];
|
||||
string name = names[index].ToLowerInvariant();
|
||||
if (amount == 1) { name = name.TrimEnd('s'); }
|
||||
|
||||
if (nonZeroIndices.Count > 1 && i == last) { builder.Append("and "); }
|
||||
builder.Append(amount);
|
||||
builder.Append(' ');
|
||||
builder.Append(name);
|
||||
if (nonZeroIndices.Count > 1 && i != last) { builder.Append(", "); }
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
internal static void ApplyRegex(InfisicalProcessResult result, Regex expression)
|
||||
{
|
||||
if (expression == null) { return; }
|
||||
if (!string.IsNullOrEmpty(result.StandardOutput))
|
||||
{
|
||||
result.StandardOutputObject = expression.Matches(result.StandardOutput).Cast<Match>().ToArray();
|
||||
}
|
||||
if (!string.IsNullOrEmpty(result.StandardError))
|
||||
{
|
||||
result.StandardErrorObject = expression.Matches(result.StandardError).Cast<Match>().ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private static void Log(IInfisicalLogger logger, string message)
|
||||
{
|
||||
if (logger != null) { logger.Verbose(Component, message); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using PSInfisicalAPI.Connections;
|
||||
using PSInfisicalAPI.Endpoints;
|
||||
using PSInfisicalAPI.Errors;
|
||||
using PSInfisicalAPI.Http;
|
||||
using PSInfisicalAPI.Logging;
|
||||
using PSInfisicalAPI.Models;
|
||||
using PSInfisicalAPI.Serialization;
|
||||
|
||||
namespace PSInfisicalAPI.SubOrganizations
|
||||
{
|
||||
public sealed class InfisicalSubOrganizationClient
|
||||
{
|
||||
private const string Component = "SubOrganizationClient";
|
||||
|
||||
private readonly IInfisicalLogger _logger;
|
||||
private readonly JsonInfisicalSerializer _serializer;
|
||||
private readonly InfisicalApiInvoker _invoker;
|
||||
|
||||
public InfisicalSubOrganizationClient(IInfisicalHttpClient httpClient, IInfisicalLogger logger)
|
||||
{
|
||||
if (httpClient == null) { throw new ArgumentNullException(nameof(httpClient)); }
|
||||
_logger = logger ?? NullInfisicalLogger.Instance;
|
||||
_serializer = new JsonInfisicalSerializer();
|
||||
_invoker = new InfisicalApiInvoker(httpClient);
|
||||
}
|
||||
|
||||
public InfisicalSubOrganization[] List(InfisicalConnection connection, int? limit, int? offset, string search, string orderBy, string orderDirection, bool? isAccessible)
|
||||
{
|
||||
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||
|
||||
List<KeyValuePair<string, string>> queryParameters = new List<KeyValuePair<string, string>>();
|
||||
if (limit.HasValue) { queryParameters.Add(new KeyValuePair<string, string>("limit", limit.Value.ToString(CultureInfo.InvariantCulture))); }
|
||||
if (offset.HasValue) { queryParameters.Add(new KeyValuePair<string, string>("offset", offset.Value.ToString(CultureInfo.InvariantCulture))); }
|
||||
if (!string.IsNullOrEmpty(search)) { queryParameters.Add(new KeyValuePair<string, string>("search", search)); }
|
||||
if (!string.IsNullOrEmpty(orderBy)) { queryParameters.Add(new KeyValuePair<string, string>("orderBy", orderBy)); }
|
||||
if (!string.IsNullOrEmpty(orderDirection)) { queryParameters.Add(new KeyValuePair<string, string>("orderDirection", orderDirection)); }
|
||||
if (isAccessible.HasValue) { queryParameters.Add(new KeyValuePair<string, string>("isAccessible", isAccessible.Value ? "true" : "false")); }
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Information(Component, "Attempting to list Infisical sub-organizations. Please Wait...");
|
||||
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.ListSubOrganizations, "ListSubOrganizations", null, queryParameters, null);
|
||||
InfisicalSubOrganizationListResponseDto dto = _serializer.Deserialize<InfisicalSubOrganizationListResponseDto>(response.Body);
|
||||
response.Clear();
|
||||
|
||||
InfisicalSubOrganization[] mapped = InfisicalSubOrganizationMapper.MapMany(dto != null ? dto.SubOrganizations : null);
|
||||
_logger.Information(Component, "Infisical sub-organization list retrieval was successful.");
|
||||
return mapped;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_logger.Error(Component, "Infisical sub-organization list retrieval failed.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public InfisicalSubOrganization Retrieve(InfisicalConnection connection, string subOrganizationId)
|
||||
{
|
||||
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||
if (string.IsNullOrEmpty(subOrganizationId)) { throw new InfisicalConfigurationException("SubOrganizationId is required."); }
|
||||
|
||||
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "subOrgId", subOrganizationId } };
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Information(Component, string.Concat("Attempting to retrieve Infisical sub-organization '", subOrganizationId, "'. Please Wait..."));
|
||||
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.RetrieveSubOrganization, "RetrieveSubOrganization", pathParameters, null, null);
|
||||
InfisicalSubOrganizationSingleResponseDto dto = _serializer.Deserialize<InfisicalSubOrganizationSingleResponseDto>(response.Body);
|
||||
response.Clear();
|
||||
|
||||
InfisicalSubOrganization mapped = InfisicalSubOrganizationMapper.Map(dto != null ? dto.SubOrganization : null);
|
||||
_logger.Information(Component, "Infisical sub-organization retrieval was successful.");
|
||||
return mapped;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_logger.Error(Component, "Infisical sub-organization retrieval failed.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public InfisicalSubOrganization Create(InfisicalConnection connection, string name, string slug)
|
||||
{
|
||||
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||
if (string.IsNullOrEmpty(name)) { throw new InfisicalConfigurationException("Name is required."); }
|
||||
if (string.IsNullOrEmpty(slug)) { throw new InfisicalConfigurationException("Slug is required."); }
|
||||
|
||||
InfisicalSubOrganizationCreateRequestDto request = new InfisicalSubOrganizationCreateRequestDto { Name = name, Slug = slug };
|
||||
string body = _serializer.Serialize(request);
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Information(Component, string.Concat("Attempting to create Infisical sub-organization '", name, "'. Please Wait..."));
|
||||
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.CreateSubOrganization, "CreateSubOrganization", null, null, body);
|
||||
InfisicalSubOrganizationSingleResponseDto dto = _serializer.Deserialize<InfisicalSubOrganizationSingleResponseDto>(response.Body);
|
||||
response.Clear();
|
||||
|
||||
InfisicalSubOrganization mapped = InfisicalSubOrganizationMapper.Map(dto != null ? dto.SubOrganization : null);
|
||||
_logger.Information(Component, "Infisical sub-organization creation was successful.");
|
||||
return mapped;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_logger.Error(Component, "Infisical sub-organization creation failed.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public InfisicalSubOrganization Update(InfisicalConnection connection, string subOrganizationId, string name, string slug)
|
||||
{
|
||||
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||
if (string.IsNullOrEmpty(subOrganizationId)) { throw new InfisicalConfigurationException("SubOrganizationId is required."); }
|
||||
|
||||
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "subOrgId", subOrganizationId } };
|
||||
InfisicalSubOrganizationUpdateRequestDto request = new InfisicalSubOrganizationUpdateRequestDto { Name = name, Slug = slug };
|
||||
string body = _serializer.Serialize(request);
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Information(Component, string.Concat("Attempting to update Infisical sub-organization '", subOrganizationId, "'. Please Wait..."));
|
||||
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.UpdateSubOrganization, "UpdateSubOrganization", pathParameters, null, body);
|
||||
InfisicalSubOrganizationSingleResponseDto dto = _serializer.Deserialize<InfisicalSubOrganizationSingleResponseDto>(response.Body);
|
||||
response.Clear();
|
||||
|
||||
InfisicalSubOrganization mapped = InfisicalSubOrganizationMapper.Map(dto != null ? dto.SubOrganization : null);
|
||||
_logger.Information(Component, "Infisical sub-organization update was successful.");
|
||||
return mapped;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_logger.Error(Component, "Infisical sub-organization update failed.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void Delete(InfisicalConnection connection, string subOrganizationId)
|
||||
{
|
||||
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||
if (string.IsNullOrEmpty(subOrganizationId)) { throw new InfisicalConfigurationException("SubOrganizationId is required."); }
|
||||
|
||||
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "subOrgId", subOrganizationId } };
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Information(Component, string.Concat("Attempting to delete Infisical sub-organization '", subOrganizationId, "'. Please Wait..."));
|
||||
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.DeleteSubOrganization, "DeleteSubOrganization", pathParameters, null, null);
|
||||
response.Clear();
|
||||
_logger.Information(Component, "Infisical sub-organization deletion was successful.");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_logger.Error(Component, "Infisical sub-organization deletion failed.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PSInfisicalAPI.SubOrganizations
|
||||
{
|
||||
internal sealed class InfisicalSubOrganizationResponseDto
|
||||
{
|
||||
[JsonProperty("id")] public string Id { get; set; }
|
||||
[JsonProperty("_id")] public string InternalId { get; set; }
|
||||
[JsonProperty("name")] public string Name { get; set; }
|
||||
[JsonProperty("slug")] public string Slug { get; set; }
|
||||
[JsonProperty("organizationId")] public string OrganizationId { get; set; }
|
||||
[JsonProperty("orgId")] public string OrgId { get; set; }
|
||||
[JsonProperty("isAccessible")] public bool IsAccessible { get; set; }
|
||||
[JsonProperty("createdAt")] public string CreatedAt { get; set; }
|
||||
[JsonProperty("updatedAt")] public string UpdatedAt { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class InfisicalSubOrganizationListResponseDto
|
||||
{
|
||||
[JsonProperty("subOrganizations")] public List<InfisicalSubOrganizationResponseDto> SubOrganizations { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class InfisicalSubOrganizationSingleResponseDto
|
||||
{
|
||||
[JsonProperty("subOrganization")] public InfisicalSubOrganizationResponseDto SubOrganization { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class InfisicalSubOrganizationCreateRequestDto
|
||||
{
|
||||
[JsonProperty("name")] public string Name { get; set; }
|
||||
[JsonProperty("slug")] public string Slug { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class InfisicalSubOrganizationUpdateRequestDto
|
||||
{
|
||||
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] public string Name { get; set; }
|
||||
[JsonProperty("slug", NullValueHandling = NullValueHandling.Ignore)] public string Slug { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using PSInfisicalAPI.Models;
|
||||
|
||||
namespace PSInfisicalAPI.SubOrganizations
|
||||
{
|
||||
internal static class InfisicalSubOrganizationMapper
|
||||
{
|
||||
public static InfisicalSubOrganization Map(InfisicalSubOrganizationResponseDto dto)
|
||||
{
|
||||
if (dto == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new InfisicalSubOrganization
|
||||
{
|
||||
Id = !string.IsNullOrEmpty(dto.Id) ? dto.Id : dto.InternalId,
|
||||
Name = dto.Name,
|
||||
Slug = dto.Slug,
|
||||
OrganizationId = !string.IsNullOrEmpty(dto.OrganizationId) ? dto.OrganizationId : dto.OrgId,
|
||||
IsAccessible = dto.IsAccessible,
|
||||
CreatedAtUtc = ParseTimestamp(dto.CreatedAt),
|
||||
UpdatedAtUtc = ParseTimestamp(dto.UpdatedAt)
|
||||
};
|
||||
}
|
||||
|
||||
public static InfisicalSubOrganization[] MapMany(IEnumerable<InfisicalSubOrganizationResponseDto> items)
|
||||
{
|
||||
if (items == null)
|
||||
{
|
||||
return Array.Empty<InfisicalSubOrganization>();
|
||||
}
|
||||
|
||||
List<InfisicalSubOrganization> results = new List<InfisicalSubOrganization>();
|
||||
foreach (InfisicalSubOrganizationResponseDto dto in items)
|
||||
{
|
||||
InfisicalSubOrganization mapped = Map(dto);
|
||||
if (mapped != null)
|
||||
{
|
||||
results.Add(mapped);
|
||||
}
|
||||
}
|
||||
|
||||
return results.ToArray();
|
||||
}
|
||||
|
||||
private static DateTimeOffset? ParseTimestamp(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
DateTimeOffset parsed;
|
||||
if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out parsed))
|
||||
{
|
||||
return parsed;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user