Honor -ErrorAction, fix pipeline-stop noise, and correct certificate request paths
Publish to PowerShell Gallery / build (pull_request) Successful in 43s
Publish to PowerShell Gallery / release (pull_request) Successful in 18s
Publish to PowerShell Gallery / publish (pull_request) Successful in 14s

BREAKING: operation failures are now non-terminating errors, so -ErrorAction
(and $ErrorActionPreference) decides the outcome. try/catch around these cmdlets
now requires -ErrorAction Stop or $ErrorActionPreference = 'Stop'. A failing
pipeline item no longer aborts the batch.

Cmdlets no longer report "The pipeline has been stopped." as an error.
Select-Object -First, and Where-Object feeding it, stop the upstream cmdlet by
design; the shared error path in InfisicalCmdletBase now lets pipeline-control
exceptions propagate untouched instead of logging them and raising an error.

Error-level diagnostics moved off the warning stream to verbose. Every
Logger.Error call site logs and then throws, so the failure already reaches the
caller as an ErrorRecord; emitting it again as eight warning lines put failures
under -WarningAction instead of -ErrorAction. One error per failure now.

Request-InfisicalCertificate:
- -CommonName accepts the RDN form (CN=WEB01) and reduces it to the bare value,
  which previously produced a CN=CN=WEB01 subject plus a bogus DNS SAN.
- -DnsName routes IP literals to iPAddress SAN entries, so the mixed output of
  Get-InfisicalSANList can be splatted in as documented.
- The CA path sends the normalized common name to the signing endpoint.
- The issuance path is resolved and reported before a keypair is generated, and
  a CA with direct issuance disabled fails fast with guidance naming
  -PkiSubscriberSlug and -CertificateProfileId. Infisical exposes no
  template-based issuance route, so no -CertificateTemplateId is added.

Get-InfisicalCertificateAuthority table output gains a DirectIssue column
(EnableDirectIssuance) so CAs eligible for -CertificateAuthorityId are visible.

README, about_PSInfisicalAPI, and cmdlet help document the stream/-ErrorAction
contract, subscriber discovery, and direct-issuance setup. Adds regression tests
for pipeline-stop propagation, logger stream routing, SAN splitting, common-name
normalization, and the non-terminating convention across all cmdlets.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-30 17:52:32 -04:00
parent 14c8c4f384
commit f62b3e90b1
65 changed files with 792 additions and 94 deletions
@@ -38,11 +38,12 @@
</ViewSelectedBy>
<TableControl>
<TableHeaders>
<TableColumnHeader><Label>Name</Label><Width>28</Width></TableColumnHeader>
<TableColumnHeader><Label>CommonName</Label><Width>32</Width></TableColumnHeader>
<TableColumnHeader><Label>Type</Label><Width>10</Width></TableColumnHeader>
<TableColumnHeader><Label>Status</Label><Width>10</Width></TableColumnHeader>
<TableColumnHeader><Label>KeyAlgorithm</Label><Width>14</Width></TableColumnHeader>
<TableColumnHeader><Label>Name</Label><Width>24</Width></TableColumnHeader>
<TableColumnHeader><Label>CommonName</Label><Width>28</Width></TableColumnHeader>
<TableColumnHeader><Label>Type</Label><Width>9</Width></TableColumnHeader>
<TableColumnHeader><Label>Status</Label><Width>8</Width></TableColumnHeader>
<TableColumnHeader><Label>DirectIssue</Label><Width>11</Width></TableColumnHeader>
<TableColumnHeader><Label>KeyAlgorithm</Label><Width>13</Width></TableColumnHeader>
<TableColumnHeader><Label>NotAfter</Label><Width>22</Width></TableColumnHeader>
</TableHeaders>
<TableRowEntries>
@@ -52,6 +53,7 @@
<TableColumnItem><PropertyName>CommonName</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>Type</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>Status</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>EnableDirectIssuance</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>KeyAlgorithm</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>NotAfter</PropertyName></TableColumnItem>
</TableColumnItems>
+2 -2
View File
@@ -1,6 +1,6 @@
@{
RootModule = 'PSInfisicalAPI.psm1'
ModuleVersion = '2026.06.16.0217'
ModuleVersion = '2026.07.30.2151'
GUID = 'b8a2f3d4-7c51-4d2f-9e6a-1f0c8b3d4e51'
Author = 'Grace Solutions'
CompanyName = 'Grace Solutions'
@@ -74,7 +74,7 @@
LicenseUri = 'https://www.gnu.org/licenses/agpl-3.0.html'
ProjectUri = 'https://prod.git.gracesolution.info/gsadmin/PSInfisicalAPI'
ReleaseNotes = 'See CHANGELOG.md in the project repository for release history.'
CommitHash = '6318d06362ad'
CommitHash = '14c8c4f3845b'
}
}
}
Binary file not shown.
@@ -1066,7 +1066,7 @@ $RemoveInfisicalTagResult = Remove-InfisicalTag @RemoveInfisicalTagParameters</d
<maml:alertSet>
<maml:title>Notes</maml:title>
<maml:alert>
<maml:para>ByID retrieval currently always resolves against the internal CA endpoint. CA Ids returned here are the values to pass on -CertificateAuthorityId to Request-InfisicalCertificate. The Type property distinguishes 'internal' from 'acme' when -Kind Any is used.</maml:para>
<maml:para>ByID retrieval currently always resolves against the internal CA endpoint. CA Ids returned here are the values to pass on -CertificateAuthorityId to Request-InfisicalCertificate. The Type property distinguishes 'internal' from 'acme' when -Kind Any is used. Only CAs whose EnableDirectIssuance property is True can sign a CSR through -CertificateAuthorityId; the others require a subscriber or certificate profile instead.</maml:para>
</maml:alert>
</maml:alertSet>
<command:examples>
@@ -1281,6 +1281,8 @@ $GetInfisicalCertificatePolicyResult = Get-InfisicalCertificatePolicy @GetInfisi
<maml:title>Notes</maml:title>
<maml:alert>
<maml:para>Default -PrivateKeyProtection is 'LocalOnly': the leaf is loaded into memory without persisting the private key and PrivateKeyPem is scrubbed from the emitted result unless -PrivateKeyPath or an explicit -KeyStorageFlags binding overrides it. The reuse path completes its chain from the Infisical bundle when local stores are incomplete; pass -LocalChainOnly to suppress that fetch entirely.</maml:para>
<maml:para>-CertificateAuthorityId only works against a CA that permits direct issuance (Get-InfisicalCertificateAuthority reports this as EnableDirectIssuance). The cmdlet resolves the issuer and validates this before generating a keypair, naming the subscriber, CA, or profile it will use on the verbose stream and in the -WhatIf target. There is no -CertificateTemplateId parameter because Infisical's REST API exposes no template-based issuance route; when the API asks for 'a certificate template or subscriber', supply -PkiSubscriberSlug or -CertificateProfileId, or enable direct issuance on the CA.</maml:para>
<maml:para>-CommonName takes the bare value ('web01.contoso.com'), not an RDN; a leading 'CN=' is stripped because the CSR builder adds the prefix itself. -DnsName accepts the mixed output of Get-InfisicalSANList: IP literals in that list are emitted as iPAddress SAN entries rather than dNSName entries.</maml:para>
</maml:alert>
</maml:alertSet>
<command:examples>
@@ -1066,7 +1066,7 @@ $RemoveInfisicalTagResult = Remove-InfisicalTag @RemoveInfisicalTagParameters</d
<maml:alertSet>
<maml:title>Notes</maml:title>
<maml:alert>
<maml:para>ByID retrieval currently always resolves against the internal CA endpoint. CA Ids returned here are the values to pass on -CertificateAuthorityId to Request-InfisicalCertificate. The Type property distinguishes 'internal' from 'acme' when -Kind Any is used.</maml:para>
<maml:para>ByID retrieval currently always resolves against the internal CA endpoint. CA Ids returned here are the values to pass on -CertificateAuthorityId to Request-InfisicalCertificate. The Type property distinguishes 'internal' from 'acme' when -Kind Any is used. Only CAs whose EnableDirectIssuance property is True can sign a CSR through -CertificateAuthorityId; the others require a subscriber or certificate profile instead.</maml:para>
</maml:alert>
</maml:alertSet>
<command:examples>
@@ -1281,6 +1281,8 @@ $GetInfisicalCertificatePolicyResult = Get-InfisicalCertificatePolicy @GetInfisi
<maml:title>Notes</maml:title>
<maml:alert>
<maml:para>Default -PrivateKeyProtection is 'LocalOnly': the leaf is loaded into memory without persisting the private key and PrivateKeyPem is scrubbed from the emitted result unless -PrivateKeyPath or an explicit -KeyStorageFlags binding overrides it. The reuse path completes its chain from the Infisical bundle when local stores are incomplete; pass -LocalChainOnly to suppress that fetch entirely.</maml:para>
<maml:para>-CertificateAuthorityId only works against a CA that permits direct issuance (Get-InfisicalCertificateAuthority reports this as EnableDirectIssuance). The cmdlet resolves the issuer and validates this before generating a keypair, naming the subscriber, CA, or profile it will use on the verbose stream and in the -WhatIf target. There is no -CertificateTemplateId parameter because Infisical's REST API exposes no template-based issuance route; when the API asks for 'a certificate template or subscriber', supply -PkiSubscriberSlug or -CertificateProfileId, or enable direct issuance on the CA.</maml:para>
<maml:para>-CommonName takes the bare value ('web01.contoso.com'), not an RDN; a leading 'CN=' is stripped because the CSR builder adds the prefix itself. -DnsName accepts the mixed output of Get-InfisicalSANList: IP literals in that list are emitted as iPAddress SAN entries rather than dNSName entries.</maml:para>
</maml:alert>
</maml:alertSet>
<command:examples>
@@ -84,6 +84,39 @@ EXAMPLES
Get-InfisicalSecrets |
Export-InfisicalSecrets -Path .\secrets.env -Format Env
ERROR HANDLING AND STREAMS
Every cmdlet derives from PSCmdlet, so the common parameters are bound:
-Verbose, -Debug, -ErrorAction, -ErrorVariable, -WarningAction,
-WarningVariable, -InformationAction, -InformationVariable, -OutVariable,
and -PipelineVariable, plus -WhatIf/-Confirm where ShouldProcess applies.
Output is separated by stream so those parameters mean what they say:
Error The failure itself, once, as a non-terminating ErrorRecord.
Warning Advisories that are not failures.
Verbose Request/response trace and the trail leading up to a failure.
Debug Low-level detail.
Operation failures are NON-TERMINATING, so -ErrorAction decides the
outcome:
Continue (default) Error is written; a pipeline keeps processing.
SilentlyContinue Nothing printed; still in $Error/-ErrorVariable.
Ignore Nothing printed and nothing recorded.
Stop Promoted to terminating; try/catch catches it.
To catch a failure you must ask for it:
try {
Request-InfisicalCertificate @Parameters -ErrorAction Stop
} catch [PSInfisicalAPI.Errors.InfisicalApiException] {
"HTTP $($_.Exception.StatusCode): $($_.Exception.ApiErrorMessage)"
}
The ErrorRecord carries the API detail, so log scraping is unnecessary:
$Error[0].Exception exposes StatusCode, ApiErrorCode, ApiErrorMessage, and
ApiRequestId on InfisicalApiException.
SECURITY NOTES
- SecureString is used for ClientSecret, AccessToken, and any secret
payloads returned by the API.