Make -ProjectId optional across PKI cmdlets, add application filtering, and fix the Quick Start #23

Merged
gsadmin merged 15 commits from dev into main 2026-08-01 02:49:46 +00:00
Owner

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:

const projects = await projectDAL.find({ orgId: actorOrgId, type: ProjectType.CertificateManager });
if (projects.length === 1) return projects[0].id;

Requiring -ProjectId on every PKI cmdlet therefore made the module stricter than the service it wraps.

-ProjectId is now optional

Applies to Get-InfisicalCertificateApplication, -ApplicationEnrollment, -Authority, -Certificate, -CertificatePolicy, -CertificateProfile, Get-InfisicalPkiSubscriber, and Request-InfisicalCertificate:

Get-InfisicalCertificateApplication
Get-InfisicalCertificateAuthority -Kind Internal
Get-InfisicalCertificateProfile -IncludeConfigs
Get-InfisicalPkiSubscriber
VERBOSE: -ProjectId was not supplied; resolved the organization's only Certificate Manager project
         'Microsoft Endpoint Configuration Manager' (2122628e-...).

An organization with more than one produces an error naming the candidates rather than guessing:

This organization has 2 Certificate Manager projects, so -ProjectId cannot be resolved automatically.
Pass it explicitly. Available: 'Platform PKI' (aaaa...), 'Lab PKI' (bbbb...).

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.

Application filtering

Get-InfisicalCertificateProfile gains -ApplicationId and -CaId, which the profiles endpoint already accepts as query filters, so a listing can be scoped the way the console groups profiles:

$Application = Get-InfisicalCertificateApplication | Where-Object {($_.Name -eq '2pint')}
Get-InfisicalCertificateProfile -ApplicationId $Application.Id -IncludeConfigs
Get-InfisicalCertificate -ApplicationId $Application.Id

Get-InfisicalCertificate already accepted -ApplicationId. The existing five-argument ListCertificateProfiles overload 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.

What it is Cmdlet
Project The Certificate Manager project itself. One per organization in most setups. Get-InfisicalProject -Type cert-manager
Application A grouping of profiles, members, and certificates inside that project. Get-InfisicalCertificateApplication

So a project mecm can contain applications 2pint and microsoft-endpoint-configuration-manager; only mecm is a project. Every application carries the ProjectId it belongs to, which is why that field is real rather than vestigial.

Fixed: the README Quick Start did not run

Connect-Infisical -BaseUri ... -ProjectId '1' -Environment 'dev' ...
  -> A parameter cannot be found that matches parameter name 'ProjectId'.

Connect-Infisical has 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 the Request-InfisicalCertificate example fixed earlier.

Verification

321 tests pass, up from 314. New tests assert -ProjectId is 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 -ProjectId short-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 -ProjectId optional on every parameter set, that -ApplicationId/-CaId are present, and that calling without -ProjectId now reaches connection handling rather than failing parameter binding.

Full build.ps1 -RunTests green, including module import, manifest, and help validation across 53 cmdlets.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

Generated with Claude Code

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: ```ts const projects = await projectDAL.find({ orgId: actorOrgId, type: ProjectType.CertificateManager }); if (projects.length === 1) return projects[0].id; ``` Requiring `-ProjectId` on every PKI cmdlet therefore made the module stricter than the service it wraps. ## `-ProjectId` is now optional Applies to `Get-InfisicalCertificateApplication`, `-ApplicationEnrollment`, `-Authority`, `-Certificate`, `-CertificatePolicy`, `-CertificateProfile`, `Get-InfisicalPkiSubscriber`, and `Request-InfisicalCertificate`: ```powershell Get-InfisicalCertificateApplication Get-InfisicalCertificateAuthority -Kind Internal Get-InfisicalCertificateProfile -IncludeConfigs Get-InfisicalPkiSubscriber ``` ```text VERBOSE: -ProjectId was not supplied; resolved the organization's only Certificate Manager project 'Microsoft Endpoint Configuration Manager' (2122628e-...). ``` An organization with more than one produces an error naming the candidates rather than guessing: ```text This organization has 2 Certificate Manager projects, so -ProjectId cannot be resolved automatically. Pass it explicitly. Available: 'Platform PKI' (aaaa...), 'Lab PKI' (bbbb...). ``` 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. ## Application filtering `Get-InfisicalCertificateProfile` gains `-ApplicationId` and `-CaId`, which the profiles endpoint already accepts as query filters, so a listing can be scoped the way the console groups profiles: ```powershell $Application = Get-InfisicalCertificateApplication | Where-Object {($_.Name -eq '2pint')} Get-InfisicalCertificateProfile -ApplicationId $Application.Id -IncludeConfigs Get-InfisicalCertificate -ApplicationId $Application.Id ``` `Get-InfisicalCertificate` already accepted `-ApplicationId`. The existing five-argument `ListCertificateProfiles` overload 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. | | What it is | Cmdlet | | --- | --- | --- | | **Project** | The Certificate Manager project itself. One per organization in most setups. | `Get-InfisicalProject -Type cert-manager` | | **Application** | A grouping of profiles, members, and certificates inside that project. | `Get-InfisicalCertificateApplication` | So a project `mecm` can contain applications `2pint` and `microsoft-endpoint-configuration-manager`; only `mecm` is a project. Every application carries the `ProjectId` it belongs to, which is why that field is real rather than vestigial. ## Fixed: the README Quick Start did not run ``` Connect-Infisical -BaseUri ... -ProjectId '1' -Environment 'dev' ... -> A parameter cannot be found that matches parameter name 'ProjectId'. ``` `Connect-Infisical` has 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 the `Request-InfisicalCertificate` example fixed earlier. ## Verification 321 tests pass, up from 314. New tests assert `-ProjectId` is 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 `-ProjectId` short-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 `-ProjectId` optional on every parameter set, that `-ApplicationId`/`-CaId` are present, and that calling without `-ProjectId` now reaches connection handling rather than failing parameter binding. Full `build.ps1 -RunTests` green, including module import, manifest, and help validation across 53 cmdlets. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Generated with [Claude Code](https://claude.com/claude-code)
gsadmin added 1 commit 2026-07-31 19:26:08 +00:00
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>
gsadmin added 1 commit 2026-07-31 19:59:00 +00:00
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>
gsadmin added 1 commit 2026-07-31 20:06:37 +00:00
An organization that has not set up Certificate Manager has nothing to list, so
resolution now returns nothing instead of throwing, and the PKI Get-* cmdlets
return quietly. -Verbose explains what happened and what to do:

  -ProjectId was not supplied and this organization has no Certificate Manager
  project, so there is nothing to resolve to. Create one in Infisical
  (Certificate Management), or pass -ProjectId to target a specific project.

Without the guard the null would have travelled to the client layer and
surfaced as "ProjectId is required" from somewhere unrelated to the cause, so
each Get-* cmdlet returns as soon as resolution comes back empty.

Request-InfisicalCertificate deliberately proceeds, because profile issuance
does not need a project: only the reuse search does, and that already degrades
to a local match with a warning when the search cannot run.

Resolution is now attempted once per cmdlet instance rather than once per
non-null result, so an organization with no project does not re-query on every
call.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gsadmin added 1 commit 2026-07-31 20:14:03 +00:00
Several examples still passed -ProjectId ($Project.Id) from a project lookup the
walkthrough no longer performs, so they read as though the parameter were still
required. Removed from the profile, policy, subscriber, and certificate
authority examples, and from the offline-fallback snippet.

The end-to-end example loses the site-specific application name, and the profile
selection collapses to one line. Its "Example output" section still showed the
project and certificate authority records the example stopped fetching, so it now
matches what the script actually emits, including the project-resolution line.

Adds a renewal example directly beneath, reusing the parameters built above so
the difference is visible as two added keys rather than a second wall of setup.
It notes that the call is safe to repeat, that reuse is matched on the issuing
profile and the SAN set so a changed profile or added name reissues, and that
-Force ignores both.

Also drops the note about $CertificateProfile versus the $Profile automatic
variable, which explained a naming choice the reader has no reason to care
about, and points the extension docs at ResolveCertManagerProjectId rather than
the ResolveProjectId name that no longer exists.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gsadmin added 1 commit 2026-07-31 20:20:08 +00:00
The script created a Certificate Manager project whenever the configured slug
did not match an existing one. Against an organization that already had one
that produced a second, which would then be unable to hold applications at all -
they are served only from the organization's active project. It now adopts the
existing project and says so, creating one only when the organization has none,
and notes when an organization has several that applications work in one.

It also seeds a certificate application and attaches the seeded profiles to it.
Applications are how the console groups profiles and how
Get-InfisicalCertificateProfile -ApplicationId scopes a lookup, so seeding
profiles without one left an environment the documented workflow could not
navigate. Profiles are attached at creation via profileIds, and an application
that already exists has only its missing profiles added.

Project discovery moves to /api/v1/projects, matching the module, and tolerates
either response shape.

Separately, the environment-variable discovery table listed ProjectId,
Environment, and SecretPath as discoverable Connect-Infisical parameters.
InfisicalEnvironmentResolver defines patterns for BaseUri, OrganizationId,
ClientId, ClientSecret, AccessToken, and ApiVersion only - the other three are
per-call parameters and were never resolved from the environment. Same defect as
the Quick Start, in the table immediately below it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gsadmin added 2 commits 2026-07-31 21:41:26 +00:00
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>
gsadmin added 1 commit 2026-08-01 00:15:38 +00:00
The release body was built solely from the CHANGELOG section, which is a
terse changelog entry rather than the account of what changed. Gitea's
pull_request webhook payload carries the description - PullRequest.Body is a
documented field on the API struct - so it is now passed to the release step as
PR_BODY and leads the release when present.

The CHANGELOG entry is kept behind a fold rather than dropped, so a release
records both the narrative and the versioned entry. With no description the
body falls back to the CHANGELOG exactly as before, and with neither it says so.

Co-author and generation trailers are stripped: they belong on the commit, not
on a published release page. The description supplies its own headings, so no
"## Changes" wrapper is added around it; the fallback branches emit one because
a bare changelog fragment needs it.

Verified by lifting the run: block straight out of the workflow and executing
it, so the test exercises the shipped code rather than a copy: a description
plus changelog produces the folded form, description-only omits the fold,
changelog-only falls back, neither produces the placeholder, and the trailers
are removed while the real content survives. Both workflows still parse as YAML
with the same three jobs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gsadmin added 1 commit 2026-08-01 00:36:06 +00:00
The seeding script was never in the repository. .gitignore carries "scripts/"
for local helper scripts, core.ignorecase is true on Windows, so Scripts/
matched it and git add -A silently skipped the file. The commit that claimed to
add the script contained only its CHANGELOG and README entries; the script
itself existed on disk and nowhere else. It now lives in Tools/, which is not
ignored, and the ignore rule says why so this cannot swallow shipped tooling
again.

The script also gains an -AccessToken parameter set alongside
-ClientId/-ClientSecret, so a throwaway instance can be seeded with the bearer
token from a signed-in browser session rather than by first creating a machine
identity. The two are mutually exclusive parameter sets, so supplying both is
rejected at binding rather than silently preferring one.

A supplied token is proven with a single read before anything is created. An
expired browser token is the common case, and failing there is much better than
failing midway through building a CA hierarchy and leaving a half-seeded
project behind; the error says as much and suggests copying a fresh one.

Renaming the internal token variable was not cosmetic. The script held the
bearer token in $Script:AccessToken, the same name as the new parameter, so
PowerShell applied the parameter's [ValidateNotNull()] to every assignment and
both authentication paths failed with "the value $null is not a valid value for
the AccessToken variable" before reaching the network. It is now
$Script:BearerToken.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gsadmin added 1 commit 2026-08-01 01:23:44 +00:00
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>
gsadmin added 1 commit 2026-08-01 02:02:32 +00:00
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>
gsadmin added 1 commit 2026-08-01 02:17:42 +00:00
A profile carries exactly one enrollment method - Infisical rejects a mixed one
with "API enrollment type cannot have EST, ACME, or SCEP configuration" - so
reaching a policy over more than one method means a profile per method, sharing
the CA and policy. Added mecm-server-client-auth-scep and
ec-server-client-auth-acme next to the three API profiles.

SCEP for the RSA policy because that is how domain-joined Windows and mobile
clients enrol without a token, which is the NDES role SCCM/MECM would otherwise
need; RSA because SCEP client support for EC keys is patchy. ACME for the EC
server/client policy, for workloads that already speak it. Code signing stays
API only: SCEP and ACME prove control of a device or a DNS name, and neither
says anything about who may sign code.

A static SCEP profile needs a challenge password of at least eight characters.
Added -ScepChallengePassword, and when it is omitted the run generates one and
prints it once at the end, because Infisical will not hand it back afterwards.

ACME sets skipDnsOwnershipVerification. These are internal names from an
internal CA, and the DNS-01 challenge proves control of a public zone, so
leaving it enforced makes the profile unusable rather than safer. The comment
says so and says when to turn it off.

Auto-renew is now on for every API profile including code signing, at fourteen
days. It is an api-only setting; neither SCEP nor ACME has an equivalent,
because both have the client drive renewal itself.

Verified against a live instance: profiles create with their config ids
populated, a re-run creates nothing and does not reissue the challenge.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gsadmin added 1 commit 2026-08-01 02:28:46 +00:00
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>
gsadmin added 1 commit 2026-08-01 02:34:29 +00:00
The line still interpolated $enrollmentType, which went away when profile
creation stopped varying by enrollment method. Every run since had adopted
existing profiles, so the create path never executed and never surfaced it.
A run against an emptied project failed there immediately.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gsadmin added 1 commit 2026-08-01 02:46:19 +00:00
Repair the changelog and write up the seeding work
Publish to PowerShell Gallery / build (pull_request) Successful in 30s
Publish to PowerShell Gallery / release (pull_request) Successful in 11s
Publish to PowerShell Gallery / publish (pull_request) Successful in 8s
22c2db4a96
Two problems, one cause. Before 62131e7 the promotion step renamed the
Unreleased heading instead of consuming it, appending "(carried forward)" on
every build - one heading had accumulated the suffix seven times. Notes were
left stranded under 103 such headings while the version sections the release
workflow extracts held only their build line, so a release would have published
nothing. 62131e7 fixed the mechanism but never repaired the backlog, and
Unreleased was empty besides.

Consolidated the stranded notes under Unreleased and collapsed duplicate
version sections: 1441 lines to 570, with every one of the 144 bullets and 16
sub-headings still present, checked by diffing the sets before and after.

Described the seeding work of the last several commits, which was undocumented:
the enrollment protocols and where they are configured, the dynamic SCEP
challenge, auto-renew by default, and the four defects the first live run
exposed.

Verified the mechanism end to end - a build promotes the notes into its version
section, leaving Unreleased empty, and the workflow's extraction returns 245
lines rather than a bare build line. 340 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gsadmin merged commit e1a0547e73 into main 2026-08-01 02:49:46 +00:00
Sign in to join this conversation.