GraceSolutions 183fb48c32 Wire SCEP MDM cmdlets into manifest, build, help, and docs
Adds Get-/Export-/Write-InfisicalScepMdmProfile(ToWmi) to CmdletsToExport in the module manifest and to the build.ps1 manifest template and expected-cmdlet probe. Adds MAML help entries (description, notes, two examples each with an OrderedDictionary splat) for all three cmdlets. Updates README's cmdlet count from 34 to 37 and the cmdlet table with one-line descriptions. CHANGELOG entry summarizes the new feature, the default SCEP URL pattern, the elevation/platform guards, and the export-vs-throw rule for -Force.
2026-06-04 17:47:00 -04:00
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 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.

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.

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.
Search-InfisicalCertificate Searches Infisical certificates with advanced 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.

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:

  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 6.6 MiB
2026-06-07 00:22:11 +00:00
Languages
C# 97.2%
PowerShell 2.8%