Certificate metadata reconciliation and a cert-manager seeding script #22

Merged
gsadmin merged 2 commits from dev into main 2026-07-31 01:01:26 +00:00
Owner

Two additions, plus a documentation correction that came out of researching them.

Request-InfisicalCertificate -Metadata

Attaches key/value pairs to the issued or reused certificate. Takes any IDictionary, so a hashtable, an [Ordered] dictionary, and a generic Dictionary[String,String] all bind:

$RequestInfisicalCertificateParameters.Metadata = [Ordered]@{
    Environment = 'Production'
    Owner       = 'Platform Engineering'
    ManagedBy   = 'Invoke-SecretStaging'
}

Only the supplied keys are reconciled, and that has to be produced client-side. Infisical's PATCH /certificates/{id} replaces a certificate's metadata wholesale — certificate-v3-service deletes every resource_metadata row for the certificate before inserting what it was sent:

await resourceMetadataDAL.delete({ certificateId }, tx);
await insertMetadataForCertificate(resourceMetadataDAL, { metadata, certificateId, orgId, tx });

Sending just the caller's keys would therefore discard everything else attached to the certificate. The module reads the current set, merges the supplied keys over it, and writes back the union, so keys it was not asked about survive and several callers can each own their own:

-Metadata @{ Owner = 'Platform' }   # certificate now has Owner
-Metadata @{ Site  = 'HQ' }         # Owner survives, Site added
-Metadata @{ Owner = 'Security' }   # Owner updated, Site survives

When nothing would change, no request is sent. Reconciliation also runs on the reuse path, so a metadata change lands without forcing reissuance.

Values are flattened to strings because the API accepts only strings (443 to "443", $True to "True", $Null to ""). Keys are trimmed and compared case-insensitively, matching how PowerShell callers supply them, and blank keys are dropped since the API rejects them.

Metadata never fails an issuance that otherwise succeeded — by the time it is applied the certificate exists and may already be installed, so a failure is a warning and the certificate is still emitted.

Adds InfisicalCertificate.Metadata and InfisicalCertificateResult.Metadata as case-insensitive dictionaries, registers the PATCH endpoint under both the cert-manager and pki route namespaces, and models the metadata array the certificate response already returns.

Scripts/Initialize-InfisicalCertManagerEnvironment.ps1

Seeds a Certificate Manager project, CA hierarchy, certificate policies, and API enrollment profiles from one declarative configuration block, taking only a base URI, client id, and client secret. Standalone: the module consumes the result but is not needed to produce it, since InfisicalConnection deliberately does not expose its access token.

Seeds an RSA hierarchy carrying server and client authentication for SCCM/MECM, and an ECDSA P-384 hierarchy carrying server, client, and code signing. Objects cross-reference by name in the configuration and resolve to ids at run time, so adding a policy or profile is an entry rather than a code change.

Creating a subordinate CA needed more than one call. Infisical self-signs on creation only for a root, and only when given an expiry; a subordinate is created with status pending-certificate, and generateIntermediateCaCertificate is exported but never invoked by the create path. The script performs the sequence itself: create, GET the CSR, sign it with the parent, then import the certificate and chain back onto the subordinate.

Idempotent by lookup on each natural key, so a re-run reports what exists and creates only what is missing. -WhatIf shows the whole plan without contacting anything beyond authentication.

Documentation correction

The README claimed a newly created CA defaults to direct issuance enabled, reasoning from the database column default. The creation service passes it explicitly:

certificateAuthorityDAL.create({ projectId, name: resolvedCaName, status, enableDirectIssuance: false }, tx)

So every CA created through the API or UI has direct issuance disabled, and nothing can enable it afterwards. Certificate profiles are not merely the recommended issuance path — they are the only workable one. Corrected in the README and the issuance-path table.

Verification

314 tests pass, up from 298. The new metadata tests pin the merge rule as a table: untouched keys survive a partial update, supplied keys win, case-insensitive collisions update in place, and empty input changes nothing. Others cover value flattening, blank-key rejection, the exact JSON the update request serializes to, and endpoint registration under both namespaces.

Confirmed under Windows PowerShell 5.1 that -Metadata binds on all three issuance parameter sets and that hashtable, [Ordered], OrderedDictionary, and Dictionary[String,String] all satisfy it, with values normalizing as expected.

The seeding script was validated by parsing it, exercising its policy payload builders against the shapes Infisical's zod schemas accept, confirming every enum literal matches a defined Infisical value, checking that all name cross-references resolve, and running -WhatIf to the point of authentication. It has not been exercised against a live Infisical instance — run -WhatIf first, then a real run against a scratch org.

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

Two additions, plus a documentation correction that came out of researching them. ## `Request-InfisicalCertificate -Metadata` Attaches key/value pairs to the issued or reused certificate. Takes any `IDictionary`, so a hashtable, an `[Ordered]` dictionary, and a generic `Dictionary[String,String]` all bind: ```powershell $RequestInfisicalCertificateParameters.Metadata = [Ordered]@{ Environment = 'Production' Owner = 'Platform Engineering' ManagedBy = 'Invoke-SecretStaging' } ``` **Only the supplied keys are reconciled**, and that has to be produced client-side. Infisical's `PATCH /certificates/{id}` replaces a certificate's metadata wholesale — `certificate-v3-service` deletes every `resource_metadata` row for the certificate before inserting what it was sent: ```ts await resourceMetadataDAL.delete({ certificateId }, tx); await insertMetadataForCertificate(resourceMetadataDAL, { metadata, certificateId, orgId, tx }); ``` Sending just the caller's keys would therefore discard everything else attached to the certificate. The module reads the current set, merges the supplied keys over it, and writes back the union, so keys it was not asked about survive and several callers can each own their own: ```powershell -Metadata @{ Owner = 'Platform' } # certificate now has Owner -Metadata @{ Site = 'HQ' } # Owner survives, Site added -Metadata @{ Owner = 'Security' } # Owner updated, Site survives ``` When nothing would change, no request is sent. Reconciliation also runs on the reuse path, so a metadata change lands without forcing reissuance. Values are flattened to strings because the API accepts only strings (`443` to `"443"`, `$True` to `"True"`, `$Null` to `""`). Keys are trimmed and compared case-insensitively, matching how PowerShell callers supply them, and blank keys are dropped since the API rejects them. Metadata never fails an issuance that otherwise succeeded — by the time it is applied the certificate exists and may already be installed, so a failure is a warning and the certificate is still emitted. Adds `InfisicalCertificate.Metadata` and `InfisicalCertificateResult.Metadata` as case-insensitive dictionaries, registers the PATCH endpoint under both the `cert-manager` and `pki` route namespaces, and models the metadata array the certificate response already returns. ## `Scripts/Initialize-InfisicalCertManagerEnvironment.ps1` Seeds a Certificate Manager project, CA hierarchy, certificate policies, and API enrollment profiles from one declarative configuration block, taking only a base URI, client id, and client secret. Standalone: the module consumes the result but is not needed to produce it, since `InfisicalConnection` deliberately does not expose its access token. Seeds an RSA hierarchy carrying server and client authentication for SCCM/MECM, and an ECDSA P-384 hierarchy carrying server, client, and code signing. Objects cross-reference by name in the configuration and resolve to ids at run time, so adding a policy or profile is an entry rather than a code change. Creating a subordinate CA needed more than one call. Infisical self-signs on creation only for a root, and only when given an expiry; a subordinate is created with status `pending-certificate`, and `generateIntermediateCaCertificate` is exported but never invoked by the create path. The script performs the sequence itself: create, `GET` the CSR, sign it with the parent, then import the certificate and chain back onto the subordinate. Idempotent by lookup on each natural key, so a re-run reports what exists and creates only what is missing. `-WhatIf` shows the whole plan without contacting anything beyond authentication. ## Documentation correction The README claimed a newly created CA defaults to direct issuance enabled, reasoning from the database column default. The creation service passes it explicitly: ```ts certificateAuthorityDAL.create({ projectId, name: resolvedCaName, status, enableDirectIssuance: false }, tx) ``` So **every** CA created through the API or UI has direct issuance disabled, and nothing can enable it afterwards. Certificate profiles are not merely the recommended issuance path — they are the only workable one. Corrected in the README and the issuance-path table. ## Verification 314 tests pass, up from 298. The new metadata tests pin the merge rule as a table: untouched keys survive a partial update, supplied keys win, case-insensitive collisions update in place, and empty input changes nothing. Others cover value flattening, blank-key rejection, the exact JSON the update request serializes to, and endpoint registration under both namespaces. Confirmed under Windows PowerShell 5.1 that `-Metadata` binds on all three issuance parameter sets and that hashtable, `[Ordered]`, `OrderedDictionary`, and `Dictionary[String,String]` all satisfy it, with values normalizing as expected. The seeding script was validated by parsing it, exercising its policy payload builders against the shapes Infisical's zod schemas accept, confirming every enum literal matches a defined Infisical value, checking that all name cross-references resolve, and running `-WhatIf` to the point of authentication. It has **not** been exercised against a live Infisical instance — run `-WhatIf` first, then a real run against a scratch org. 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 2 commits 2026-07-31 00:54:57 +00:00
Stands up a complete Certificate Manager environment from one declarative
configuration block, taking only a base URI, client id, and client secret.
Standalone: the module consumes the result but is not needed to produce it,
since InfisicalConnection deliberately does not expose its access token.

Seeds an RSA hierarchy carrying server and client authentication for SCCM/MECM,
and an ECDSA P-384 hierarchy carrying server, client, and code signing. Objects
are cross-referenced by name in the configuration and resolved to ids at run
time, so adding a policy or profile means adding an entry rather than editing
code.

Creating a subordinate CA needed more than one call. Infisical only self-signs
on creation for a root, and only when given an expiry; a subordinate is created
with status pending-certificate and generateIntermediateCaCertificate is never
invoked by the create path. The script performs the sequence itself: create,
GET the CSR, sign it with the parent, then import the certificate and chain
back onto the subordinate.

Idempotent by lookup on each natural key, so a re-run reports what exists and
creates only what is missing, and -WhatIf shows the whole plan without
contacting anything beyond authentication.

Also corrects a documentation error this research surfaced. The README claimed a
newly created CA defaults to direct issuance enabled, on the strength of the
database column default. The creation service passes enableDirectIssuance:false
explicitly, so every CA created through the API or UI has it disabled and
nothing can enable it afterwards - which makes certificate profiles the only
workable issuance path, not merely the recommended one.

Validated by parsing the script, exercising the policy payload builders against
the shapes Infisical's zod schemas accept, confirming every enum literal matches
a defined Infisical value, checking that all name cross-references resolve, and
running -WhatIf to the point of authentication. Not yet exercised against a live
Infisical instance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add -Metadata to Request-InfisicalCertificate, reconciling only the supplied keys
Publish to PowerShell Gallery / build (pull_request) Successful in 31s
Publish to PowerShell Gallery / release (pull_request) Successful in 11s
Publish to PowerShell Gallery / publish (pull_request) Successful in 9s
93b0cc1924
Takes any IDictionary, so a hashtable, an [Ordered] dictionary, or a generic
Dictionary[String,String] all bind. Verified against all three issuance
parameter sets under Windows PowerShell 5.1.

The reconcile is client-side by necessity. Infisical's PATCH /certificates/{id}
replaces a certificate's metadata wholesale - certificate-v3-service deletes
every resource_metadata row for the certificate before inserting what it was
sent - so sending just the caller's keys would silently discard everything else
attached to it. The module reads the current set, merges the supplied keys over
it, and writes back the union. Keys it was not asked about survive, and when
nothing would change no request is sent at all.

Reconciliation runs on the reuse path too, so changing metadata does not force
a reissuance to take effect.

Values are flattened to strings because the API accepts only strings: 443
becomes "443", $True becomes "True", $Null becomes "". Keys are trimmed and
compared case-insensitively, matching how PowerShell callers supply them, and
blank keys are dropped since the API rejects them.

Metadata never fails an issuance that otherwise succeeded. By the time it is
applied the certificate exists and may already be installed in the store, so a
failure is reported as a warning and the certificate is still emitted.

Adds InfisicalCertificate.Metadata and InfisicalCertificateResult.Metadata as
case-insensitive dictionaries, the PATCH endpoint under both the cert-manager
and pki route namespaces, and models the metadata array already returned by the
certificate response.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gsadmin merged commit 83b79b9109 into main 2026-07-31 01:01:26 +00:00
Sign in to join this conversation.