BREAKING: Removed Get-InfisicalProjects, Get-InfisicalEnvironments, Get-InfisicalFolders, Get-InfisicalTags, Get-InfisicalSecrets, and Get-InfisicalCertificates. Their list behavior is now the default parameter set on the singular cmdlets; supplying the identity parameter switches to single-record retrieval. No back-compat aliases.
Fix: SignCertificateBySubscriber endpoint resolved to /api/v1/pki/subscribers/{subscriberName}/sign-certificate (was /pki/pki-subscribers and /cert-manager/pki-subscribers, both 404).
Added Get-InfisicalPkiSubscriber (List/ByName), InfisicalPkiSubscriber model, DTOs, mapper, and InfisicalPkiClient.ListPkiSubscribers/GetPkiSubscriber. MAML help refreshed for all consolidated cmdlets with 2 straight-line + 1 OrderedDictionary splat examples each. README extended with extension guide. CHANGELOG updated. 230/230 tests pass.
e15f650 on main exited with code -1 and zero dotnet output).
PSInfisicalAPI
A C# binary PowerShell module for interacting with the Infisical REST API. It provides cmdlets for authentication, secret retrieval, structured export, and includes automatic environment-variable discovery so connections can be established with little or no inline configuration.
- License: AGPL-3.0
- Author: Grace Solutions
- Target framework: .NET Standard 2.0 (compatible with Windows PowerShell 5.1 and PowerShell 7+)
Installation
From the PowerShell Gallery
Install-Module -Name PSInfisicalAPI -Scope CurrentUser
Import-Module -Name PSInfisicalAPI
From source
git clone https://prod.git.gracesolution.info/gsadmin/PSInfisicalAPI.git
cd PSInfisicalAPI
pwsh -NoProfile -ExecutionPolicy Bypass -File .\build.ps1 -RunTests
Import-Module -Name .\Module\PSInfisicalAPI
Cmdlets
The module exports 34 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.
| Area | Cmdlets |
|---|---|
| Session | Connect-Infisical, Disconnect-Infisical |
| Secrets | Get-InfisicalSecret, New-InfisicalSecret, Update-InfisicalSecret, Remove-InfisicalSecret, Copy-InfisicalSecret, ConvertTo-InfisicalSecretDictionary, Export-InfisicalSecrets |
| Projects | Get-InfisicalProject, New-InfisicalProject, Update-InfisicalProject, Remove-InfisicalProject |
| Environments | Get-InfisicalEnvironment, New-InfisicalEnvironment, Update-InfisicalEnvironment, Remove-InfisicalEnvironment |
| Folders | Get-InfisicalFolder, New-InfisicalFolder, Update-InfisicalFolder, Remove-InfisicalFolder |
| Tags | Get-InfisicalTag, New-InfisicalTag, Update-InfisicalTag, Remove-InfisicalTag |
| PKI | Get-InfisicalCertificateAuthority, Get-InfisicalPkiSubscriber, Get-InfisicalCertificate, Search-InfisicalCertificate, Request-InfisicalCertificate, ConvertTo-InfisicalCertificate, Install-InfisicalCertificate, Uninstall-InfisicalCertificate, Export-InfisicalCertificate |
Use Get-Help <Cmdlet> -Full for parameter details and Get-Help about_PSInfisicalAPI for the module overview.
Quick start
$secureSecret = Read-Host -AsSecureString 'Client Secret'
$connection = Connect-Infisical `
-BaseUri 'https://app.infisical.com' `
-OrganizationId '00000000-0000-0000-0000-000000000000' `
-ProjectId '11111111-1111-1111-1111-111111111111' `
-Environment 'dev' `
-ClientId 'machine-identity-client-id' `
-ClientSecret $secureSecret `
-PassThru
Get-InfisicalSecret -SecretPath '/'
Disconnect-Infisical
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.
Scope precedence
Scopes are searched in order; the first matching variable with a non-blank value wins:
ProcessUserMachine
Patterns
The resolver matches case-insensitively against patterns aligned with Infisical's CLI defaults plus common variants such as CLOUDINIT_INFISICAL_* and custom-prefixed names (e.g., myapp_infisical_client_id).
| Parameter | Example variable names matched |
|---|---|
BaseUri |
INFISICAL_API_URL, INFISICAL_BASE_URL, INFISICAL_HOST |
OrganizationId |
INFISICAL_ORG_ID, INFISICAL_ORGANIZATION_ID |
ProjectId |
INFISICAL_PROJECT_ID, INFISICAL_WORKSPACE_ID |
Environment |
INFISICAL_ENVIRONMENT, INFISICAL_ENV, INFISICAL_ENV_SLUG |
ClientId |
INFISICAL_CLIENT_ID, INFISICAL_UNIVERSAL_AUTH_CLIENT_ID |
ClientSecret |
INFISICAL_CLIENT_SECRET, INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET |
AccessToken |
INFISICAL_TOKEN, INFISICAL_ACCESS_TOKEN, INFISICAL_AUTH_TOKEN |
SecretPath |
INFISICAL_SECRET_PATH, INFISICAL_DEFAULT_SECRET_PATH |
ApiVersion |
INFISICAL_API_VERSION |
Sensitive values (ClientSecret, AccessToken) are read directly into a read-only SecureString and never logged.
Zero-configuration example
[Environment]::SetEnvironmentVariable('INFISICAL_API_URL', 'https://app.infisical.com', 'User')
[Environment]::SetEnvironmentVariable('INFISICAL_ORG_ID', '00000000-0000-0000-0000-000000000000', 'User')
[Environment]::SetEnvironmentVariable('INFISICAL_PROJECT_ID', '11111111-1111-1111-1111-111111111111', 'User')
[Environment]::SetEnvironmentVariable('INFISICAL_ENVIRONMENT', 'dev', 'User')
[Environment]::SetEnvironmentVariable('INFISICAL_CLIENT_ID', 'machine-identity-client-id', 'User')
[Environment]::SetEnvironmentVariable('INFISICAL_CLIENT_SECRET', 'super-secret-value', 'User')
Connect-Infisical
Get-InfisicalSecret
Mixed example (explicit values override discovery)
Explicit parameters always win over discovered values; blank/whitespace explicit values trigger discovery.
Connect-Infisical -Environment 'prod' # everything else discovered from environment
Logging
The resolver emits a single verbose line announcing the scan and one informational line per discovered variable (variable name and scope; values are never logged). Use -Verbose to see the scan announcement.
Building
pwsh -NoProfile -ExecutionPolicy Bypass -File .\build.ps1 -RunTests
The script builds the binary, runs unit tests, publishes binaries into Module/PSInfisicalAPI/bin/, regenerates the manifest, and validates that the module imports.
Extending the module
Adding a new API endpoint
All HTTP routes live in two files under src/PSInfisicalAPI/Endpoints/:
InfisicalEndpointNames.csdeclares aconst stringidentifier for each endpoint.InfisicalEndpointRegistry.csmaps each identifier to one or moreInfisicalEndpointDefinitionrecords grouped by resource (RegisterAuthentication,RegisterSecrets,RegisterPki, etc.).
To add a route:
- Add a constant in
InfisicalEndpointNames.cs(e.g.,public const string ListPkiSubscribers = "ListPkiSubscribers";). - In the matching
Register<Resource>method, callAdd(map, new InfisicalEndpointDefinition { ... })withName,Resource,Version,Method,Template, and theRequiresAuthorization/ContainsSecretMaterialInRequest/ContainsSecretMaterialInResponseflags. Use{placeholder}tokens inTemplate; they are substituted from thepathParametersdictionary passed by the caller. - If the same logical operation has more than one upstream path (legacy + current), register both definitions under the same
Name—InvokeWithCandidateFallbacktries each in order until one succeeds. - Invoke the endpoint from the appropriate client (
InfisicalPkiClient,InfisicalSecretsClient, etc.) via_invoker.InvokeWithCandidateFallback(connection, InfisicalEndpointNames.XYZ, "XYZ", pathParameters, query, body).
Adding a new cmdlet
Cmdlets live in src/PSInfisicalAPI/Cmdlets/ and derive from InfisicalCmdletBase, which exposes HttpClient, Logger, ResolveProjectId, and ThrowTerminatingForException. Follow the consolidated discovery pattern when the cmdlet supports both list and single-record retrieval:
[Cmdlet(VerbsCommon.Get, "InfisicalPkiSubscriber", DefaultParameterSetName = "List")]
[OutputType(typeof(InfisicalPkiSubscriber))]
public sealed class GetInfisicalPkiSubscriberCmdlet : InfisicalCmdletBase
{
[Parameter(ParameterSetName = "ByName", Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true)]
[Alias("SubscriberName", "Slug")]
public string Name { get; set; }
[Parameter] public string ProjectId { get; set; }
protected override void ProcessRecord() { /* dispatch on ParameterSetName */ }
}
After adding (or removing) a cmdlet:
-
Update
build.ps1in two places — theCmdletsToExportarray inside the generated manifest block, and the$expectedCmdsarray used byTest-ModuleImports. Both must list the same cmdlets; the build fails fast if they drift. -
Add a
<command:command>entry inModule/PSInfisicalAPI/en-US/PSInfisicalAPI.dll-Help.xml. Each entry must include a non-empty<maml:description>synopsis (do not let it start with the cmdlet name — the validation gate rejects PowerShell's auto-generated fallback), a non-empty<maml:description>body, and at least one<command:example>with a non-empty<dev:code>block. -
For consolidated
List/ single-record cmdlets, ship three examples: two straight-line invocations (one per parameter set) and oneOrderedDictionarysplat. The splat must construct the dictionary withOrdinalIgnoreCaseso parameter names round-trip case-insensitively:$Params = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase) $Params.ProjectId = (Get-InfisicalProject | Select-Object -First 1).Id $Result = Get-InfisicalPkiSubscriber @Params -
Add a
## Unreleasedentry toCHANGELOG.mddescribing the change (mark removals of public cmdlets or parameters as BREAKING). -
Run
./build.ps1 -RunTests. The script enforces the cmdlet list, runs the xUnit suite, and verifies that every exported cmdlet has a valid synopsis, description, and at least one non-empty example.
Continuous integration
.gitea/workflows/publish-psgallery.yml publishes the module to the PowerShell Gallery whenever a pull request is merged into main. The workflow expects a repository secret named PSGALLERY_API_KEY containing a valid Gallery API key.
License
Distributed under the GNU Affero General Public License v3.0. See LICENSE.