Make -ProjectId optional across PKI cmdlets, add application filtering, and fix the Quick Start #23
Reference in New Issue
Block a user
Delete Branch "dev"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The Certificate Manager console never asks which project to use. The project sits in the URL —
/organizations/{orgId}/projects/cert-manager/{projectId}/applications— but it is chosen for you, and everything below it is presented as applications. Infisical's own resolver does exactly that:Requiring
-ProjectIdon every PKI cmdlet therefore made the module stricter than the service it wraps.-ProjectIdis now optionalApplies to
Get-InfisicalCertificateApplication,-ApplicationEnrollment,-Authority,-Certificate,-CertificatePolicy,-CertificateProfile,Get-InfisicalPkiSubscriber, andRequest-InfisicalCertificate:An organization with more than one produces an error naming the candidates rather than guessing:
Resolution is client-side rather than deferred to the server, because several PKI endpoints carry the project in the URL path —
/api/v1/projects/{projectId}/pki-subscribersand.../certificates/search— and cannot fall back to the server's resolver at all. Doing it in one place keeps path-scoped and query-scoped endpoints behaving identically.Get-InfisicalCertificate -SerialNumberno longer resolves a project, since addressing a certificate by serial does not need one.Application filtering
Get-InfisicalCertificateProfilegains-ApplicationIdand-CaId, which the profiles endpoint already accepts as query filters, so a listing can be scoped the way the console groups profiles:Get-InfisicalCertificatealready accepted-ApplicationId. The existing five-argumentListCertificateProfilesoverload is retained.Projects contain applications
Worth stating plainly, because it is the confusing part: listing projects returns one entry while the console shows several applications, and those are different levels.
Get-InfisicalProject -Type cert-managerGet-InfisicalCertificateApplicationSo a project
mecmcan contain applications2pintandmicrosoft-endpoint-configuration-manager; onlymecmis a project. Every application carries theProjectIdit belongs to, which is why that field is real rather than vestigial.Fixed: the README Quick Start did not run
Connect-Infisicalhas no-ProjectId,-Environment, or-SecretPath. Project, environment, and secret path are per-call parameters, and the Quick Start now reflects that. Same class of defect as theRequest-InfisicalCertificateexample fixed earlier.Verification
321 tests pass, up from 314. New tests assert
-ProjectIdis not mandatory on any of the eight PKI cmdlets, that each one actually calls the resolver (optional without resolution would just send an empty project), that an explicit-ProjectIdshort-circuits before any lookup, that retrieval by serial does not resolve, and that the original profile-listing overload survives.Confirmed against the built module under Windows PowerShell 5.1 that all eight report
-ProjectIdoptional on every parameter set, that-ApplicationId/-CaIdare present, and that calling without-ProjectIdnow reaches connection handling rather than failing parameter binding.Full
build.ps1 -RunTestsgreen, including module import, manifest, and help validation across 53 cmdlets.Co-Authored-By: Claude Opus 5 noreply@anthropic.com
Generated with Claude Code
The Infisical console never asks which Certificate Manager project to use. The project is in the URL, but it is chosen implicitly - the service resolver takes the single cert-manager project when an organization has exactly one - and everything below it is presented as applications. Requiring -ProjectId on every PKI cmdlet made the module stricter than the service it wraps. -ProjectId is now optional on Get-InfisicalCertificateApplication, -ApplicationEnrollment, -Authority, -Certificate, -CertificatePolicy, -CertificateProfile, Get-InfisicalPkiSubscriber, and Request-InfisicalCertificate. Omitting it applies the same rule the server applies and reports the resolved project on the verbose stream; an organization with several produces an error naming the candidates rather than guessing. Resolution is client-side rather than deferred to the server because several PKI endpoints carry the project in the URL path - /api/v1/projects/{projectId}/ pki-subscribers and .../certificates/search - and cannot fall back to the server's resolver at all. Doing it in one place keeps path-scoped and query-scoped endpoints behaving identically. Get-InfisicalCertificate -SerialNumber no longer resolves a project, since addressing a certificate by serial does not need one. Adds -ApplicationId and -CaId to Get-InfisicalCertificateProfile, which the profiles endpoint already supports as query filters, so a listing can be scoped the way the console groups profiles. Get-InfisicalCertificate already accepted -ApplicationId. The existing five-argument ListCertificateProfiles overload is retained. Also fixes the README Quick Start, which did not run: it passed -ProjectId and -Environment to Connect-Infisical, which has neither, failing with "A parameter cannot be found that matches parameter name 'ProjectId'". Project, environment, and secret path are per-call parameters. Documents that a project contains applications, since listing projects returning one entry while the console shows several applications is the confusing part. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>Several Certificate Manager projects in one organization is a normal configuration, not an ambiguity to reject. Infisical designates one as the organization's active project and serves certificate applications only from it: if (req.internalCertManagerProjectId !== activeProjectId) { throw new BadRequestError({ message: "Applications are only available on this organization's active Certificate Manager project." }); } So an application-centric workflow is single-project by design, and resolving to the active project is what makes it work. Resolution now picks that project when several exist, falling back to the first - saying so on the verbose stream - when the organization designates none. Only an organization with no Certificate Manager project at all still errors, because there is genuinely nothing to resolve to. Adds InfisicalOrganization.DefaultCertManagerProjectId, which is what the organization record calls its active project, so the choice is read rather than guessed. Moves Get-InfisicalProject onto /api/v1/projects. /api/v1/workspace mounts Infisical's deprecated project router; it is retained as a fallback candidate so older servers keep working, and the endpoint shape test now expects the current route. The end-to-end README example drops to the four calls that actually do the work: find the application, pick its profile, gather SANs, request. The project lookup is gone because -ProjectId resolves itself, and the CA lookup is gone because the profile already binds its issuing CA and -InstallChain installs the chain regardless. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>Update-Changelog inserted the version heading above the notes, so the section the release workflow extracts contained only the build line while the actual notes stayed under "Unreleased". Every release published an empty changelog. It now promotes whatever sits under Unreleased into the version section and leaves a fresh empty Unreleased above it, with the provenance line italicised and last so it does not read as another entry in whichever section the notes ended on. Re-running the same version remains a no-op, and a build with nothing to say still produces a section. SignCertificateByCa now prefers /api/v1/cert-manager over the older /api/v1/pki route. RetrieveCertificate and GetCertificateBundle deliberately keep the older route first, which is the opposite of what it looks like. The /api/v1/pki route resolves a certificate by serial number, which is what callers supply; the cert-manager route takes a certificate ID and passes it through as getCert({ id }), so it cannot answer a serial. Both remain registered so either identifier resolves - which is what lets the metadata path look a certificate up by ID - and a comment now records why reordering them would break serial lookups. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>The module was read-only for PKI configuration, which is why standing up an environment meant raw REST rather than cmdlets. Adds 15: New-, Set-, and Remove- for certificate authorities, policies, profiles, applications, and PKI subscribers. New-InfisicalCertificateAuthority -Type Intermediate returns an authority that can actually issue. Infisical creates a subordinate with status pending-certificate and never invokes generateIntermediateCaCertificate from the create path, so the cmdlet performs the remaining sequence: read the certificate signing request, sign it with -ParentCaId, import the signed certificate and chain back. Policy and profile bodies are deeply nested, so the constraint objects and per-enrollment-type config blocks are taken as dictionaries rather than as dozens of parameters, matching how -Subject and -Metadata already work. Two conversion details matter and are now pinned by tests: - PowerShell callers capitalise hashtable keys, and @{ Required = ... } was reaching the API as "Required", which its schema does not recognise. The constraint vocabulary is emitted lower-cased while every other key keeps its camelCase, since those are API field names supplied verbatim and lowercasing ttlDays or isCA would silently drop them. - An empty collection is omitted rather than sent. "allowed": [] reads to Infisical as "allow nothing", never what @{ Allowed = @() } was meant to express, and an entry carrying only "type" is rejected outright. -EnrollmentConfig routes to the block matching -EnrollmentType so EST, ACME, and SCEP settings arrive through one parameter. -AutoRenew is sent only when bound, because a switch is false when absent and sending it unconditionally would disable renewal on an update that never mentioned it. -ProjectId is optional on all 15 and resolves as it does elsewhere. Every one supports -WhatIf; Remove- cmdlets default to high confirm impact. Also fixes Update-Changelog, which inserted the version heading above the notes so the section the release workflow extracts held only the build line while the notes stayed under Unreleased - every release published an empty changelog. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>Seeding a real instance failed with "Route GET:/api/v2/organizations not found". The route genuinely does not exist: /api/v2/organizations mounts only /:organizationId/* sub-routes, and the listing lives on the singular GET /api/v1/organization, which returns the same { organizations: [...] } shape. The module had the same defect, so Get-InfisicalOrganization would have 404d on any current server. ListOrganizations now prefers /api/v1/organization with the v2 route retained as a fallback candidate, and the organization client invokes with candidate fallback so the fallback can actually be reached. The endpoint shape test expected the broken template and now expects the working one. The script reported this as "The supplied -AccessToken was rejected ... it may have expired", which sent the reader after a token that was seconds old and perfectly valid. A 404 or a connection failure says nothing about the token, so only a 401 or 403 now produces that message; anything else reports that the instance could not be reached and states plainly that it is not a token problem. The token probe also moves to /api/v1/projects, which is the next call the script makes anyway, so a successful probe proves both the token and a route the run actually depends on rather than one chosen only for validation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>Ran the script against a live Infisical instance for the first time. It failed at four separate points, each fixed here. Responses are read through Get-ApiProperty/Get-ApiCollection/Get-ApiObject rather than directly. Set-StrictMode makes an absent property terminating, and the cert-manager routes return bare payloads - a naked array for a list, the resource itself for a create - where the secrets and pki routes wrap theirs in a named property. All eighteen reads now handle either shape. The application-to-profile listing is a join row carrying profileId, not a profile object with an id, so it is read accordingly. Renamed the local $configuration inside the CA loop to $caConfiguration. PowerShell variable names are case-insensitive, so from the second CA onward it shadowed the script-level $Configuration block and every lookup into it failed. Renamed the $profile loop variables for the same reason: $profile is an automatic variable. Policies now carry subject, sans, and signature algorithms, and profiles carry defaults. The previous comment claimed omitting subject and sans left them unconstrained; the opposite is true. Infisical refuses any attribute with no policy entry ("no subject policies defined"), refuses a policy that defines no signature algorithm, and refuses a request missing a usage the policy marks required - Request-InfisicalCertificate sends none of its own, so the profile default has to supply it. The enumeration list in the header dropped domain_component, upn, and any_purpose, which the API does not accept. Verified end to end: a clean run builds both CA hierarchies including the subordinate CSR-sign-import sequence, and a re-run creates nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>The enrollment methods were not appearing in the UI because they were being set in the wrong place. A profile's own enrollmentType is one value, but that is only the base; what the UI lists under an application - "configure how this application will issue certificates via API, EST, ACME, or SCEP" - is a separate configuration on the application-to-profile link, and those are independent. A single profile can answer all four at once. That replaces the previous approach. The mecm-...-scep and ec-...-acme profiles are gone; there are three profiles again, one per policy, and each declares an Enrollment block naming the protocols it should answer. Applied with PUT to /applications/{id}/profiles/{id}/enrollment/{api,scep,acme}, which is idempotent by nature, so a re-run restates the same settings. The routes take PUT, not POST, and a POST there returns 404. SCEP now uses a dynamic challenge, so there is no shared secret to distribute or rotate; each request collects a one-time password from the challenge endpoint. -ScepChallengePassword is kept for anyone who sets ChallengeType to static, and is otherwise unused. Enabling a protocol is what makes Infisical mint its endpoint - a SCEP URL, challenge URL and RA certificate, or an ACME directory URL - so the run reads them back and prints them, and returns them under Enrollment. None of it can be derived from the configuration alone. Verified against a live instance: scepConfigured and acmeConfigured come back true on the intended profiles, the dynamic challenge settings round-trip, and a re-run restates without creating anything. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>