From c9c8a8446b01a792151dccaae62a8729b9cb6825 Mon Sep 17 00:00:00 2001 From: GraceSolutions Date: Thu, 4 Jun 2026 16:56:40 -0400 Subject: [PATCH] Add -Kind switch to Get-InfisicalCertificateAuthority List parameter set gains -Kind Internal|Acme|Any. Internal (default) preserves current behavior against /api/v1/cert-manager/ca/internal. Any binds to the generic /api/v1/cert-manager/ca endpoint returning both internal and ACME CAs. Acme uses the generic endpoint and client-side filters to type=acme. ById retrieval is unchanged and still resolves against the internal CA endpoint. The existing InfisicalCertificateAuthority model already exposes a Type property to distinguish entries when -Kind Any is used. MAML help updated. --- CHANGELOG.md | 1 + .../en-US/PSInfisicalAPI.dll-Help.xml | 13 ++++--- .../GetInfisicalCertificateAuthorityCmdlet.cs | 34 ++++++++++++++++++- .../Endpoints/InfisicalEndpointNames.cs | 2 ++ .../Endpoints/InfisicalEndpointRegistry.cs | 10 ++++++ src/PSInfisicalAPI/Pki/InfisicalPkiClient.cs | 30 ++++++++++++++++ 6 files changed, 85 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f1f8a2..5bb41c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) loos - Infisical API error responses are now parsed to surface the server-side `message`, `error`, and `reqId` fields. The 4xx/5xx exception message includes the human-readable explanation (e.g. "The project is of type secret-manager") instead of an opaque `Infisical API returned 400 (Bad Request)`. The `InfisicalApiException` gains `ApiErrorMessage` and `ApiRequestId` properties; `InfisicalErrorDetails` carries the same fields so PowerShell error records and logger output expose them. - `Get-InfisicalCertificateProfile` added with `List` (default) and `ById` parameter sets. List binds to `GET /api/v1/cert-manager/certificate-profiles` (optional `-Limit`, `-Offset`, `-IncludeConfigs`); ById binds to `GET /api/v1/cert-manager/certificate-profiles/{certificateProfileId}`. New `InfisicalCertificateProfile` model surfaces ca/policy ids, slug, enrollment type, per-profile defaults (ttl, key/extended key usages), and the embedded CA/policy/apiConfig summaries. - `Get-InfisicalCertificatePolicy` added with `List` (default) and `ById` parameter sets. List binds to `GET /api/v1/cert-manager/certificate-policies` (optional `-Limit`, `-Offset`); ById binds to `GET /api/v1/cert-manager/certificate-policies/{certificatePolicyId}`. New `InfisicalCertificatePolicy` model surfaces subject, SANs, key usages, extended key usages, algorithms, and validity. Polymorphic string-or-array fields (`allowed`, `required`, `keyAlgorithm`) are normalized to arrays; `sans` is normalized whether the API returns an object or an array. +- `Get-InfisicalCertificateAuthority` gains a `-Kind` parameter on the List parameter set with values `Internal` (default, preserves prior behavior against `/api/v1/cert-manager/ca/internal`), `Any` (binds to the generic `/api/v1/cert-manager/ca` endpoint which returns both internal and ACME CAs), and `Acme` (uses the generic endpoint and client-side filters to ACME issuers only). ById retrieval is unchanged and still resolves against the internal CA endpoint. ## 2026.06.04.1920 diff --git a/Module/PSInfisicalAPI/en-US/PSInfisicalAPI.dll-Help.xml b/Module/PSInfisicalAPI/en-US/PSInfisicalAPI.dll-Help.xml index 0501b7c..82c903b 100644 --- a/Module/PSInfisicalAPI/en-US/PSInfisicalAPI.dll-Help.xml +++ b/Module/PSInfisicalAPI/en-US/PSInfisicalAPI.dll-Help.xml @@ -1006,17 +1006,17 @@ $RemoveInfisicalTagResult = Remove-InfisicalTag @RemoveInfisicalTagParameters Get-InfisicalCertificateAuthority - Lists or retrieves Infisical internal Certificate Authorities. + Lists or retrieves Infisical Certificate Authorities. Get InfisicalCertificateAuthority - When -CaId is supplied (ById parameter set) returns a single CA. Otherwise (List parameter set) returns every internal CA visible in the project. -ProjectId defaults to the session-pinned project when omitted. + When -CaId is supplied (ById parameter set) returns a single internal CA. Otherwise (List parameter set) returns CAs scoped by -Kind: Internal (default, /api/v1/cert-manager/ca/internal), Any (/api/v1/cert-manager/ca returning both internal and ACME), or Acme (filters the generic endpoint to ACME issuers only). -ProjectId defaults to the session-pinned project when omitted. Notes - Only internal CAs are surfaced; external/ACME issuers are not enumerated by this cmdlet. CA Ids returned here are the values to pass on -CertificateAuthorityId to Request-InfisicalCertificate. + 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. @@ -1027,6 +1027,11 @@ $RemoveInfisicalTagResult = Remove-InfisicalTag @RemoveInfisicalTagParameters EXAMPLE 2 + Get-InfisicalCertificateAuthority -Kind Any + Lists every CA (internal and ACME) visible in the session-pinned project; inspect the Type property to distinguish them. + + + EXAMPLE 3 $GetInfisicalCertificateAuthorityListResult = Get-InfisicalCertificateAuthority | Where-Object { $_.FriendlyName -eq 'Issuing CA - Platform' } $GetInfisicalCertificateAuthorityParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase) @@ -1035,7 +1040,7 @@ $GetInfisicalCertificateAuthorityParameters.ProjectId = $ConnectInfisicalParamet $GetInfisicalCertificateAuthorityParameters.Verbose = $True $GetInfisicalCertificateAuthorityResult = Get-InfisicalCertificateAuthority @GetInfisicalCertificateAuthorityParameters - Filters the CA list by friendly name and then re-fetches the canonical CA record by id. + Filters the CA list by friendly name and then re-fetches the canonical CA record by id using a splatted parameter set. diff --git a/src/PSInfisicalAPI/Cmdlets/GetInfisicalCertificateAuthorityCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/GetInfisicalCertificateAuthorityCmdlet.cs index bd90777..3857d9a 100644 --- a/src/PSInfisicalAPI/Cmdlets/GetInfisicalCertificateAuthorityCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/GetInfisicalCertificateAuthorityCmdlet.cs @@ -16,6 +16,10 @@ namespace PSInfisicalAPI.Cmdlets [Parameter] public string ProjectId { get; set; } + [Parameter(ParameterSetName = "List")] + [ValidateSet("Internal", "Acme", "Any")] + public string Kind { get; set; } = "Internal"; + protected override void ProcessRecord() { try @@ -34,7 +38,20 @@ namespace PSInfisicalAPI.Cmdlets return; } - InfisicalCertificateAuthority[] all = client.ListInternalCertificateAuthorities(connection, ProjectId); + InfisicalCertificateAuthority[] all; + if (string.Equals(Kind, "Internal", StringComparison.OrdinalIgnoreCase)) + { + all = client.ListInternalCertificateAuthorities(connection, ProjectId); + } + else + { + all = client.ListAllCertificateAuthorities(connection, ProjectId); + if (string.Equals(Kind, "Acme", StringComparison.OrdinalIgnoreCase)) + { + all = FilterByType(all, "acme"); + } + } + foreach (InfisicalCertificateAuthority ca in all) { WriteObject(ca); @@ -45,5 +62,20 @@ namespace PSInfisicalAPI.Cmdlets ThrowTerminatingForException("GetInfisicalCertificateAuthorityCmdlet", "GetCertificateAuthority", exception); } } + + private static InfisicalCertificateAuthority[] FilterByType(InfisicalCertificateAuthority[] source, string type) + { + if (source == null || source.Length == 0) { return Array.Empty(); } + System.Collections.Generic.List kept = new System.Collections.Generic.List(); + foreach (InfisicalCertificateAuthority ca in source) + { + if (ca != null && string.Equals(ca.Type, type, StringComparison.OrdinalIgnoreCase)) + { + kept.Add(ca); + } + } + + return kept.ToArray(); + } } } diff --git a/src/PSInfisicalAPI/Endpoints/InfisicalEndpointNames.cs b/src/PSInfisicalAPI/Endpoints/InfisicalEndpointNames.cs index 78f934d..7ccdd03 100644 --- a/src/PSInfisicalAPI/Endpoints/InfisicalEndpointNames.cs +++ b/src/PSInfisicalAPI/Endpoints/InfisicalEndpointNames.cs @@ -60,5 +60,7 @@ namespace PSInfisicalAPI.Endpoints public const string ListCertificatePolicies = "ListCertificatePolicies"; public const string GetCertificatePolicy = "GetCertificatePolicy"; + + public const string ListCertificateAuthorities = "ListCertificateAuthorities"; } } diff --git a/src/PSInfisicalAPI/Endpoints/InfisicalEndpointRegistry.cs b/src/PSInfisicalAPI/Endpoints/InfisicalEndpointRegistry.cs index 985bab4..06abc80 100644 --- a/src/PSInfisicalAPI/Endpoints/InfisicalEndpointRegistry.cs +++ b/src/PSInfisicalAPI/Endpoints/InfisicalEndpointRegistry.cs @@ -682,6 +682,16 @@ namespace PSInfisicalAPI.Endpoints Template = "/api/v1/cert-manager/certificate-policies/{certificatePolicyId}", RequiresAuthorization = true }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.ListCertificateAuthorities, + Resource = "Pki", + Version = "v1", + Method = "GET", + Template = "/api/v1/cert-manager/ca", + RequiresAuthorization = true + }); } public static InfisicalEndpointDefinition Get(string name) diff --git a/src/PSInfisicalAPI/Pki/InfisicalPkiClient.cs b/src/PSInfisicalAPI/Pki/InfisicalPkiClient.cs index 50efb1e..65e52b2 100644 --- a/src/PSInfisicalAPI/Pki/InfisicalPkiClient.cs +++ b/src/PSInfisicalAPI/Pki/InfisicalPkiClient.cs @@ -91,6 +91,36 @@ namespace PSInfisicalAPI.Pki } } + public InfisicalCertificateAuthority[] ListAllCertificateAuthorities(InfisicalConnection connection, string projectId) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + + List> query = new List> + { + new KeyValuePair("projectId", resolvedProjectId) + }; + + try + { + _logger.Information(Component, "Attempting to list Infisical certificate authorities. Please Wait..."); + InfisicalHttpResponse response = _invoker.InvokeWithCandidateFallback(connection, InfisicalEndpointNames.ListCertificateAuthorities, "ListCertificateAuthorities", null, query, null); + string body = response.Body; + response.Clear(); + + List source = ParseCaListBody(body); + InfisicalCertificateAuthority[] mapped = InfisicalCaMapper.MapMany(source, resolvedProjectId); + _logger.Information(Component, "Infisical certificate authority list retrieval was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical certificate authority list retrieval failed."); + throw; + } + } + public InfisicalCertificate RetrieveCertificate(InfisicalConnection connection, string identifier) { if (connection == null) { throw new ArgumentNullException(nameof(connection)); }