2026-06-02 15:51:28 +00:00

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

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 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

Cmdlet Purpose
Connect-Infisical Establishes an authenticated session with an Infisical server and stores it for use by subsequent cmdlets.
Disconnect-Infisical Clears the current Infisical session from the module-level session manager.

Secrets

Cmdlet Purpose
Get-InfisicalSecret Lists or retrieves Infisical secrets within a project, environment, and optional folder path.
New-InfisicalSecret Creates a new Infisical secret, with support for SecureString values and bulk creation.
Update-InfisicalSecret Updates an existing Infisical secret value, comment, name, or tags.
Remove-InfisicalSecret Deletes one or many Infisical secrets by name.
Copy-InfisicalSecret Duplicates one or more secrets into a different environment or secret path.
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
Get-InfisicalProject Lists or retrieves Infisical projects accessible to the current identity.
New-InfisicalProject Creates a new Infisical project in the active organization.
Update-InfisicalProject Updates the name, description, or auto-capitalization flag on an existing project.
Remove-InfisicalProject Deletes an Infisical project.

Environments

Cmdlet Purpose
Get-InfisicalEnvironment Lists or retrieves Infisical environments defined on a project.
New-InfisicalEnvironment Creates a new environment on an Infisical project.
Update-InfisicalEnvironment Updates the name, slug, or sort order of an existing Infisical environment.
Remove-InfisicalEnvironment Deletes an Infisical environment from a project.

Folders

Cmdlet Purpose
Get-InfisicalFolder Lists or retrieves Infisical folders at a given secret path.
New-InfisicalFolder Creates a new Infisical folder under the supplied parent path.
Update-InfisicalFolder Renames an existing Infisical folder.
Remove-InfisicalFolder Deletes an Infisical folder and all secrets it contains.

Tags

Cmdlet Purpose
Get-InfisicalTag Lists or retrieves Infisical tags defined on a project.
New-InfisicalTag Creates a new Infisical tag on a project.
Update-InfisicalTag Updates the slug, name, or color of an existing Infisical tag.
Remove-InfisicalTag Deletes an Infisical tag from a project.

PKI

Cmdlet Purpose
Get-InfisicalCertificateAuthority Lists or retrieves Infisical internal Certificate Authorities.
Get-InfisicalPkiSubscriber Lists or retrieves Infisical PKI subscribers in a project.
Get-InfisicalCertificate Lists or retrieves Infisical certificates in a project, with optional filters and automatic paging.
Request-InfisicalCertificate Requests a new Infisical certificate (local CSR + sign) or reuses a still-valid existing one.
ConvertTo-InfisicalCertificate Materializes an X509Certificate2 from an Infisical certificate record, bundle, or serial number.
Install-InfisicalCertificate Installs an Infisical certificate (and optional chain) into a Windows certificate store.
Uninstall-InfisicalCertificate Removes a certificate from a Windows certificate store by thumbprint, subject, or pipeline input.
Export-InfisicalCertificate Exports an Infisical certificate to disk in PEM, PFX, or CER format.
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.

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

End-to-end: request and install a chained certificate

Connects, selects a cert-manager project, sources SANs from Get-InfisicalSANList, requests a certificate through a PKI subscriber, 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.

$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 -Type cert-manager | Where-Object {($_.Name -eq 'Platform')} | Select-Object -First 1
$Subscriber = Get-InfisicalPkiSubscriber -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.PkiSubscriberSlug = $Subscriber.Name
    $RequestInfisicalCertificateParameters.CommonName       = $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.Install           = $True
    $RequestInfisicalCertificateParameters.InstallChain      = $True
    $RequestInfisicalCertificateParameters.Verbose           = $True

$Certificate = Request-InfisicalCertificate @RequestInfisicalCertificateParameters

$Null = Disconnect-Infisical -Verbose

Choosing an issuance path

Request-InfisicalCertificate has three mutually exclusive issuance parameter sets. Which one works depends on how the project is configured in Infisical:

Parameter Use when
-PkiSubscriberSlug The project defines PKI subscribers (Get-InfisicalPkiSubscriber). Preferred for most setups.
-CertificateProfileId The project issues through certificate profiles (Get-InfisicalCertificateProfile).
-CertificateAuthorityId Signing straight against a CA. Requires direct issuance to be enabled on that CA.

There is no -CertificateTemplateId parameter. Infisical's REST API exposes no template-based issuance route — templates are consumed internally by EST and subscribers — so when the API says "Certificate template or subscriber is required for issuance", the reachable answers are a subscriber, a profile, or enabling direct issuance.

The cmdlet resolves and reports the issuer before generating a keypair, so -Verbose tells you exactly what will sign the request:

VERBOSE: [...] - [Information] - [RequestInfisicalCertificateCmdlet] - Issuing via PKI subscriber 'web-tier' in project '2122628e-...'.
VERBOSE: [...] - [Information] - [RequestInfisicalCertificateCmdlet] - Issuing directly via certificate authority 'intermediate-ca' (bf661d78-...); direct issuance is enabled.

-WhatIf names the same issuer without issuing anything:

Request-InfisicalCertificate @RequestInfisicalCertificateParameters -WhatIf
# What if: Performing the operation "Request new certificate" on target
#          "PKI subscriber 'web-tier' for CN=WEB01".

Discovering subscribers

A subscriber is a named enrollment identity that pins the CA, TTL, key usages, and SAN policy, so the request carries only a CSR. List what a project offers:

Get-InfisicalPkiSubscriber -ProjectId ($Project.Id) |
    Format-Table Name, CommonName, Status, Ttl, CaId

Pass the subscriber's Name to -PkiSubscriberSlug. Because the subscriber owns the lifetime and usage policy, -Ttl, -KeyUsage, and -ExtendedKeyUsage are not accepted on this parameter set — set them on the subscriber in Infisical instead.

If the project has no subscribers, either create one (Certificate Management > Subscribers > Add Subscriber) or use one of the other two paths.

Enabling direct issuance on a CA

Direct issuance lets a CA sign a bare CSR with no subscriber or template in front of it. It is a per-CA setting, and Infisical rejects the request with 400 Certificate template or subscriber is required for issuance when it is off. This module now catches that before building a CSR:

Request-InfisicalCertificate : Certificate authority 'intermediate-ca' (bf661d78-...) has direct issuance
disabled, so it cannot sign a CSR on its own. Either enable direct issuance on the CA in Infisical
(Certificate Authorities > the CA > Enable Direct Issuance), or issue through a subscriber or profile
instead: Request-InfisicalCertificate -PkiSubscriberSlug <name> ... or -CertificateProfileId <id> ...

To turn it on, in the Infisical UI open the cert-manager project, go to Certificate Authorities, select the CA, and enable Direct Issuance in its settings. The setting is surfaced by this module as EnableDirectIssuance, so you can confirm it and pick an eligible CA in one step:

Get-InfisicalCertificateAuthority -ProjectId ($Project.Id) -Kind Internal |
    Format-Table Name, CommonName, Status, EnableDirectIssuance

$Ca = Get-InfisicalCertificateAuthority -ProjectId ($Project.Id) -Kind Internal |
    Where-Object {($_.EnableDirectIssuance -eq $True)} |
    Select-Object -First 1

$RequestInfisicalCertificateParameters.CertificateAuthorityId = $Ca.Id
$RequestInfisicalCertificateParameters.Ttl                    = '90d'   # required by the CA path

Prefer a subscriber or profile for routine enrollment: direct issuance bypasses the naming and key-usage constraints those layers enforce. Reserve it for bootstrap or break-glass cases.

Subject and SAN handling

  • -CommonName takes the bare value (WEB01.contoso.com), not an RDN. CN=WEB01 is accepted and normalized, since the CSR builder adds the CN= prefix itself.
  • Get-InfisicalSANList returns DNS names and IP addresses in one list. Passing the whole list to -DnsName is fine: IP literals are detected and emitted as iPAddress SAN entries rather than malformed dNSName entries.
  • -Ttl (or -NotAfter) applies to the -CertificateAuthorityId and -CertificateProfileId paths. Subscriber-issued certificates take their lifetime from the subscriber definition.

Diagnostics and error handling

Every cmdlet derives from PSCmdlet, so the full set of common parameters is bound: -Verbose, -Debug, -ErrorAction, -ErrorVariable, -WarningAction, -WarningVariable, -InformationAction, -InformationVariable, -OutVariable, -PipelineVariable, and -WhatIf/-Confirm on the cmdlets that declare SupportsShouldProcess.

Output is routed by stream so those parameters mean what they say:

Stream Carries Controlled by
Error The failure itself, once, as a non-terminating ErrorRecord -ErrorAction, -ErrorVariable, 2>
Warning Genuine advisories that are not failures (e.g. issuance returned no certificate) -WarningAction, -WarningVariable
Verbose Request/response trace and the diagnostic trail leading up to a failure -Verbose
Debug Low-level detail -Debug

A failed call surfaces exactly one error. The [Error]-tagged diagnostic lines that precede it are on the verbose stream, so they appear only under -Verbose and never compete with the ErrorRecord:

# One error, no warning noise.
Request-InfisicalCertificate @Parameters -ErrorVariable Failure -ErrorAction SilentlyContinue

# The ErrorRecord carries the API detail; no log scraping required.
$Failure[0].Exception.StatusCode      # 400
$Failure[0].Exception.ApiErrorCode    # BadRequest
$Failure[0].Exception.ApiErrorMessage # Certificate template or subscriber is required for issuance
$Failure[0].Exception.ApiRequestId    # req-SSPFN1gc2zHvkV

-ErrorAction decides the outcome

Operation failures are reported as non-terminating errors, so -ErrorAction (or $ErrorActionPreference) governs what happens, exactly as it does for built-in cmdlets:

-ErrorAction Behavior
Continue (default) Error is written; a pipeline keeps processing its remaining input
SilentlyContinue Nothing is printed; the error is still in $Error and -ErrorVariable
Ignore Nothing is printed and nothing is recorded in $Error
Stop Promoted to a terminating error that try/catch catches
Inquire Prompts

A failing item does not abort the batch:

'web01', 'does-not-exist', 'web02' |
    ForEach-Object { Get-InfisicalPkiSubscriber -ProjectId $ProjectId -Name $_ -ErrorAction SilentlyContinue }
# emits web01 and web02; the failure is available in $Error

To catch failures you must ask for it with -ErrorAction Stop or $ErrorActionPreference = 'Stop':

try {
    $Certificate = Request-InfisicalCertificate @Parameters -ErrorAction Stop
} catch [PSInfisicalAPI.Errors.InfisicalApiException] {
    Write-Warning "Issuance failed with HTTP $($_.Exception.StatusCode): $($_.Exception.ApiErrorMessage)"
}

Breaking change. Failures were previously terminating, so try/catch caught them without -ErrorAction Stop. Existing try/catch blocks need -ErrorAction Stop added (or $ErrorActionPreference = 'Stop' set) to keep catching.

Exception types are InfisicalApiException, InfisicalAuthenticationException, InfisicalHttpException, InfisicalSerializationException, InfisicalConfigurationException, InfisicalExportException, and InfisicalImportException, all deriving from InfisicalException.

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:

  1. Process
  2. User
  3. Machine

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.cs declares a const string identifier for each endpoint.
  • InfisicalEndpointRegistry.cs maps each identifier to one or more InfisicalEndpointDefinition records grouped by resource (RegisterAuthentication, RegisterSecrets, RegisterPki, etc.).

To add a route:

  1. Add a constant in InfisicalEndpointNames.cs (e.g., public const string ListPkiSubscribers = "ListPkiSubscribers";).
  2. In the matching Register<Resource> method, call Add(map, new InfisicalEndpointDefinition { ... }) with Name, Resource, Version, Method, Template, and the RequiresAuthorization / ContainsSecretMaterialInRequest / ContainsSecretMaterialInResponse flags. Use {placeholder} tokens in Template; they are substituted from the pathParameters dictionary passed by the caller.
  3. If the same logical operation has more than one upstream path (legacy + current), register both definitions under the same NameInvokeWithCandidateFallback tries each in order until one succeeds.
  4. 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:

  1. Update build.ps1 in two places — the CmdletsToExport array inside the generated manifest block, and the $expectedCmds array used by Test-ModuleImports. Both must list the same cmdlets; the build fails fast if they drift.

  2. Add a <command:command> entry in Module/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.

  3. For consolidated List / single-record cmdlets, ship three examples: two straight-line invocations (one per parameter set) and one OrderedDictionary splat. The splat must construct the dictionary with OrdinalIgnoreCase so 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
    
  4. Add a ## Unreleased entry to CHANGELOG.md describing the change (mark removals of public cmdlets or parameters as BREAKING).

  5. 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.

Committing source and build artifacts in lockstep

The embedded BuildCommitHash in Module/PSInfisicalAPI/PSInfisicalAPI.psd1 and the bundled DLL is captured from git rev-parse HEAD at build time. To keep the embedded hash truthful, commit source and build artifacts as two ordered commits:

  1. Stage and commit your source changes first. Suppose this produces commit S.
  2. Run ./build.ps1 -RunTests -CommitArtifacts. The build picks up S as HEAD, embeds it as BuildCommitHash, then stages and commits only the build outputs (Module/PSInfisicalAPI/bin/**, Module/PSInfisicalAPI/PSInfisicalAPI.psd1, and the CHANGELOG.md build-stamp insertion). The commit message references S so the binary commit always traces back to its source.
  3. git push.

-CommitArtifacts only touches the three artifact paths above; any other dirty files in your working tree are left alone. Use the older -CommitOnSuccess switch only when you intentionally want a single commit covering everything (git add -A + git commit -m "Build <version>"); the two switches are mutually exclusive.

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.

S
Description
PSInfisicalAPI is a C# binary PowerShell module for the Infisical REST API, providing cmdlets for authentication, secret retrieval, and export with automatic environment-variable discovery across Process, User, and Machine scopes.
https://www.powershellgallery.com/packages/PSInfisicalAPI
Readme AGPL-3.0 11 MiB
2026-08-01 02:50:33 +00:00
Languages
C# 92.2%
PowerShell 7.8%