Consolidate Get-Infisical* singular/plural pairs; fix PKI subscriber 404; add Get-InfisicalPkiSubscriber

BREAKING: Removed Get-InfisicalProjects, Get-InfisicalEnvironments, Get-InfisicalFolders, Get-InfisicalTags, Get-InfisicalSecrets, and Get-InfisicalCertificates. Their list behavior is now the default parameter set on the singular cmdlets; supplying the identity parameter switches to single-record retrieval. No back-compat aliases.

Fix: SignCertificateBySubscriber endpoint resolved to /api/v1/pki/subscribers/{subscriberName}/sign-certificate (was /pki/pki-subscribers and /cert-manager/pki-subscribers, both 404).

Added Get-InfisicalPkiSubscriber (List/ByName), InfisicalPkiSubscriber model, DTOs, mapper, and InfisicalPkiClient.ListPkiSubscribers/GetPkiSubscriber. MAML help refreshed for all consolidated cmdlets with 2 straight-line + 1 OrderedDictionary splat examples each. README extended with extension guide. CHANGELOG updated. 230/230 tests pass.
This commit is contained in:
GraceSolutions
2026-06-04 15:11:49 -04:00
parent 51bf819c37
commit 2489b7adca
28 changed files with 919 additions and 972 deletions
@@ -115,17 +115,15 @@ namespace PSInfisicalAPI.Tests
}
[Fact]
public void Candidates_For_SignCertificateBySubscriber_Include_Pki_And_CertManager()
public void SignCertificateBySubscriber_Uses_Pki_Subscribers_Template()
{
IReadOnlyList<InfisicalEndpointDefinition> candidates = InfisicalEndpointRegistry.GetCandidates(InfisicalEndpointNames.SignCertificateBySubscriber);
Assert.Contains(candidates, c => c.Template == "/api/v1/pki/pki-subscribers/{subscriberName}/sign-certificate");
Assert.Contains(candidates, c => c.Template == "/api/v1/cert-manager/pki-subscribers/{subscriberName}/sign-certificate");
foreach (InfisicalEndpointDefinition candidate in candidates)
{
Assert.Equal("POST", candidate.Method);
Assert.True(candidate.RequiresAuthorization);
Assert.True(candidate.ContainsSecretMaterialInResponse);
}
Assert.Single(candidates);
InfisicalEndpointDefinition only = candidates[0];
Assert.Equal("/api/v1/pki/subscribers/{subscriberName}/sign-certificate", only.Template);
Assert.Equal("POST", only.Method);
Assert.True(only.RequiresAuthorization);
Assert.True(only.ContainsSecretMaterialInResponse);
}
[Fact]
@@ -12,7 +12,7 @@ namespace PSInfisicalAPI.Tests
private static readonly Assembly ModuleAssembly = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly;
[Fact]
public void GetInfisicalCertificate_Cmdlet_Is_Singular_With_Mandatory_SerialNumber()
public void GetInfisicalCertificate_Cmdlet_Is_Singular_With_SerialNumber_In_Single_ParameterSet()
{
Type cmdletType = ModuleAssembly.GetType("PSInfisicalAPI.Cmdlets.GetInfisicalCertificateCmdlet", true);
Assert.True(typeof(PSInfisicalAPI.Cmdlets.InfisicalCmdletBase).IsAssignableFrom(cmdletType));
@@ -27,6 +27,13 @@ namespace PSInfisicalAPI.Tests
Assert.Equal(VerbsCommon.Get, cmdletData.ConstructorArguments[0].Value);
Assert.Equal("InfisicalCertificate", cmdletData.ConstructorArguments[1].Value);
string defaultParameterSetName = null;
foreach (CustomAttributeNamedArgument named in cmdletData.NamedArguments)
{
if (named.MemberName == "DefaultParameterSetName") { defaultParameterSetName = (string)named.TypedValue.Value; break; }
}
Assert.Equal("List", defaultParameterSetName);
PropertyInfo serialProp = cmdletType.GetProperty("SerialNumber");
Assert.NotNull(serialProp);
@@ -38,33 +45,27 @@ namespace PSInfisicalAPI.Tests
Assert.NotNull(parameterAttr);
bool mandatory = false;
string parameterSetName = null;
foreach (CustomAttributeNamedArgument named in parameterAttr.NamedArguments)
{
if (named.MemberName == "Mandatory") { mandatory = (bool)named.TypedValue.Value; break; }
if (named.MemberName == "Mandatory") { mandatory = (bool)named.TypedValue.Value; }
else if (named.MemberName == "ParameterSetName") { parameterSetName = (string)named.TypedValue.Value; }
}
Assert.True(mandatory);
Assert.Equal("Single", parameterSetName);
}
[Fact]
public void GetInfisicalCertificates_Cmdlet_Is_Registered_For_Listing()
public void GetInfisicalCertificate_Cmdlet_Exposes_List_Filter_Properties()
{
Type cmdletType = ModuleAssembly.GetType("PSInfisicalAPI.Cmdlets.GetInfisicalCertificatesCmdlet", true);
Assert.True(typeof(PSInfisicalAPI.Cmdlets.InfisicalCmdletBase).IsAssignableFrom(cmdletType));
CustomAttributeData cmdletData = null;
foreach (CustomAttributeData candidate in cmdletType.GetCustomAttributesData())
{
if (candidate.AttributeType == typeof(CmdletAttribute)) { cmdletData = candidate; break; }
}
Assert.NotNull(cmdletData);
Assert.Equal(VerbsCommon.Get, cmdletData.ConstructorArguments[0].Value);
Assert.Equal("InfisicalCertificates", cmdletData.ConstructorArguments[1].Value);
Type cmdletType = ModuleAssembly.GetType("PSInfisicalAPI.Cmdlets.GetInfisicalCertificateCmdlet", true);
Assert.NotNull(cmdletType.GetProperty("CommonName"));
Assert.NotNull(cmdletType.GetProperty("FriendlyName"));
Assert.NotNull(cmdletType.GetProperty("CaId"));
Assert.NotNull(cmdletType.GetProperty("Limit"));
Assert.NotNull(cmdletType.GetProperty("Offset"));
Assert.NotNull(cmdletType.GetProperty("NoAutoPage"));
Assert.NotNull(cmdletType.GetProperty("List"));
}
[Fact]
@@ -6,14 +6,24 @@ using PSInfisicalAPI.Pki;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.Get, "InfisicalCertificate")]
[Cmdlet(VerbsCommon.Get, "InfisicalCertificate", DefaultParameterSetName = "List")]
[OutputType(typeof(InfisicalCertificate))]
public sealed class GetInfisicalCertificateCmdlet : InfisicalCmdletBase
{
[Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true)]
[Parameter(ParameterSetName = "Single", Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true)]
[Alias("Id", "Identifier")]
public string SerialNumber { get; set; }
[Parameter(ParameterSetName = "List")] public SwitchParameter List { get; set; }
[Parameter(ParameterSetName = "List")] public string ProjectId { get; set; }
[Parameter(ParameterSetName = "List")] public string CommonName { get; set; }
[Parameter(ParameterSetName = "List")] public string FriendlyName { get; set; }
[Parameter(ParameterSetName = "List")] public string Status { get; set; }
[Parameter(ParameterSetName = "List")] public string[] CaId { get; set; }
[Parameter(ParameterSetName = "List")] public int? Limit { get; set; }
[Parameter(ParameterSetName = "List")] public int? Offset { get; set; }
[Parameter(ParameterSetName = "List")] public SwitchParameter NoAutoPage { get; set; }
protected override void ProcessRecord()
{
try
@@ -21,10 +31,57 @@ namespace PSInfisicalAPI.Cmdlets
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
InfisicalPkiClient client = new InfisicalPkiClient(HttpClient, Logger);
InfisicalCertificate cert = client.RetrieveCertificate(connection, SerialNumber);
if (cert != null)
if (string.Equals(ParameterSetName, "Single", StringComparison.Ordinal))
{
WriteObject(cert);
InfisicalCertificate cert = client.RetrieveCertificate(connection, SerialNumber);
if (cert != null)
{
WriteObject(cert);
}
return;
}
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
InfisicalCertificateSearchQuery query = new InfisicalCertificateSearchQuery
{
ProjectId = resolvedProjectId,
CommonName = CommonName,
FriendlyName = FriendlyName,
Status = Status,
CaIds = CaId,
Limit = Limit ?? 100,
Offset = Offset ?? 0
};
int requestedLimit = query.Limit ?? 100;
int emitted = 0;
while (true)
{
InfisicalCertificateSearchResult page = client.SearchCertificates(connection, query);
if (page == null || page.Certificates == null || page.Certificates.Length == 0)
{
break;
}
foreach (InfisicalCertificate cert in page.Certificates)
{
WriteObject(cert);
emitted++;
}
if (NoAutoPage.IsPresent || page.Certificates.Length < requestedLimit)
{
break;
}
if (page.TotalCount > 0 && emitted >= page.TotalCount)
{
break;
}
query.Offset = (query.Offset ?? 0) + page.Certificates.Length;
}
}
catch (Exception exception)
@@ -1,76 +0,0 @@
using System;
using System.Management.Automation;
using PSInfisicalAPI.Connections;
using PSInfisicalAPI.Models;
using PSInfisicalAPI.Pki;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.Get, "InfisicalCertificates")]
[OutputType(typeof(InfisicalCertificate))]
public sealed class GetInfisicalCertificatesCmdlet : InfisicalCmdletBase
{
[Parameter] public string ProjectId { get; set; }
[Parameter] public string CommonName { get; set; }
[Parameter] public string FriendlyName { get; set; }
[Parameter] public string Status { get; set; }
[Parameter] public string[] CaId { get; set; }
[Parameter] public int? Limit { get; set; }
[Parameter] public int? Offset { get; set; }
[Parameter] public SwitchParameter NoAutoPage { get; set; }
protected override void ProcessRecord()
{
try
{
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
InfisicalPkiClient client = new InfisicalPkiClient(HttpClient, Logger);
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
InfisicalCertificateSearchQuery query = new InfisicalCertificateSearchQuery
{
ProjectId = resolvedProjectId,
CommonName = CommonName,
FriendlyName = FriendlyName,
Status = Status,
CaIds = CaId,
Limit = Limit ?? 100,
Offset = Offset ?? 0
};
int requestedLimit = query.Limit ?? 100;
int emitted = 0;
while (true)
{
InfisicalCertificateSearchResult page = client.SearchCertificates(connection, query);
if (page == null || page.Certificates == null || page.Certificates.Length == 0)
{
break;
}
foreach (InfisicalCertificate cert in page.Certificates)
{
WriteObject(cert);
emitted++;
}
if (NoAutoPage.IsPresent || page.Certificates.Length < requestedLimit)
{
break;
}
if (page.TotalCount > 0 && emitted >= page.TotalCount)
{
break;
}
query.Offset = (query.Offset ?? 0) + page.Certificates.Length;
}
}
catch (Exception exception)
{
ThrowTerminatingForException("GetInfisicalCertificatesCmdlet", "GetCertificates", exception);
}
}
}
}
@@ -6,16 +6,18 @@ using PSInfisicalAPI.Models;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.Get, "InfisicalEnvironment")]
[Cmdlet(VerbsCommon.Get, "InfisicalEnvironment", DefaultParameterSetName = "List")]
[OutputType(typeof(InfisicalEnvironment))]
public sealed class GetInfisicalEnvironmentCmdlet : InfisicalCmdletBase
{
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
[Parameter(ParameterSetName = "Single", Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
[Alias("Slug", "Id", "Environment")]
public string EnvironmentSlugOrId { get; set; }
[Parameter] public string ProjectId { get; set; }
[Parameter(ParameterSetName = "List")] public SwitchParameter List { get; set; }
protected override void ProcessRecord()
{
try
@@ -23,15 +25,27 @@ namespace PSInfisicalAPI.Cmdlets
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger);
InfisicalEnvironment env = client.Retrieve(connection, resolvedProjectId, EnvironmentSlugOrId);
if (env != null)
if (string.Equals(ParameterSetName, "Single", StringComparison.Ordinal))
{
InfisicalEnvironment env = client.Retrieve(connection, resolvedProjectId, EnvironmentSlugOrId);
if (env != null)
{
WriteObject(env);
}
return;
}
InfisicalEnvironment[] envs = client.List(connection, resolvedProjectId);
foreach (InfisicalEnvironment env in envs)
{
WriteObject(env);
}
}
catch (Exception exception)
{
ThrowTerminatingForException("GetInfisicalEnvironmentCmdlet", "RetrieveEnvironment", exception);
ThrowTerminatingForException("GetInfisicalEnvironmentCmdlet", "GetEnvironment", exception);
}
}
}
@@ -1,34 +0,0 @@
using System;
using System.Management.Automation;
using PSInfisicalAPI.Connections;
using PSInfisicalAPI.Environments;
using PSInfisicalAPI.Models;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.Get, "InfisicalEnvironments")]
[OutputType(typeof(InfisicalEnvironment))]
public sealed class GetInfisicalEnvironmentsCmdlet : InfisicalCmdletBase
{
[Parameter] public string ProjectId { get; set; }
protected override void ProcessRecord()
{
try
{
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger);
InfisicalEnvironment[] envs = client.List(connection, resolvedProjectId);
foreach (InfisicalEnvironment env in envs)
{
WriteObject(env);
}
}
catch (Exception exception)
{
ThrowTerminatingForException("GetInfisicalEnvironmentsCmdlet", "ListEnvironments", exception);
}
}
}
}
@@ -6,11 +6,11 @@ using PSInfisicalAPI.Models;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.Get, "InfisicalFolder")]
[Cmdlet(VerbsCommon.Get, "InfisicalFolder", DefaultParameterSetName = "List")]
[OutputType(typeof(InfisicalFolder))]
public sealed class GetInfisicalFolderCmdlet : InfisicalCmdletBase
{
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
[Parameter(ParameterSetName = "Single", Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
[Alias("Name", "Id")]
public string FolderNameOrId { get; set; }
@@ -18,6 +18,8 @@ namespace PSInfisicalAPI.Cmdlets
[Parameter] public string Environment { get; set; }
[Parameter] public string Path { get; set; }
[Parameter(ParameterSetName = "List")] public SwitchParameter List { get; set; }
protected override void ProcessRecord()
{
try
@@ -27,15 +29,27 @@ namespace PSInfisicalAPI.Cmdlets
string resolvedEnvironment = ResolveEnvironment(connection, Environment);
string resolvedPath = ResolveSecretPath(connection, Path);
InfisicalFolderClient client = new InfisicalFolderClient(HttpClient, Logger);
InfisicalFolder folder = client.Retrieve(connection, resolvedProjectId, resolvedEnvironment, resolvedPath, FolderNameOrId);
if (folder != null)
if (string.Equals(ParameterSetName, "Single", StringComparison.Ordinal))
{
InfisicalFolder folder = client.Retrieve(connection, resolvedProjectId, resolvedEnvironment, resolvedPath, FolderNameOrId);
if (folder != null)
{
WriteObject(folder);
}
return;
}
InfisicalFolder[] folders = client.List(connection, resolvedProjectId, resolvedEnvironment, resolvedPath);
foreach (InfisicalFolder folder in folders)
{
WriteObject(folder);
}
}
catch (Exception exception)
{
ThrowTerminatingForException("GetInfisicalFolderCmdlet", "RetrieveFolder", exception);
ThrowTerminatingForException("GetInfisicalFolderCmdlet", "GetFolder", exception);
}
}
}
@@ -1,38 +0,0 @@
using System;
using System.Management.Automation;
using PSInfisicalAPI.Connections;
using PSInfisicalAPI.Folders;
using PSInfisicalAPI.Models;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.Get, "InfisicalFolders")]
[OutputType(typeof(InfisicalFolder))]
public sealed class GetInfisicalFoldersCmdlet : InfisicalCmdletBase
{
[Parameter] public string ProjectId { get; set; }
[Parameter] public string Environment { get; set; }
[Parameter] public string Path { get; set; }
protected override void ProcessRecord()
{
try
{
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
string resolvedEnvironment = ResolveEnvironment(connection, Environment);
string resolvedPath = ResolveSecretPath(connection, Path);
InfisicalFolderClient client = new InfisicalFolderClient(HttpClient, Logger);
InfisicalFolder[] folders = client.List(connection, resolvedProjectId, resolvedEnvironment, resolvedPath);
foreach (InfisicalFolder folder in folders)
{
WriteObject(folder);
}
}
catch (Exception exception)
{
ThrowTerminatingForException("GetInfisicalFoldersCmdlet", "ListFolders", exception);
}
}
}
}
@@ -0,0 +1,50 @@
using System;
using System.Management.Automation;
using PSInfisicalAPI.Connections;
using PSInfisicalAPI.Models;
using PSInfisicalAPI.Pki;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.Get, "InfisicalPkiSubscriber", DefaultParameterSetName = "List")]
[OutputType(typeof(InfisicalPkiSubscriber))]
public sealed class GetInfisicalPkiSubscriberCmdlet : InfisicalCmdletBase
{
[Parameter(ParameterSetName = "ByName", Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true)]
[Alias("SubscriberName", "Slug")]
public string Name { get; set; }
[Parameter] public string ProjectId { get; set; }
protected override void ProcessRecord()
{
try
{
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
InfisicalPkiClient client = new InfisicalPkiClient(HttpClient, Logger);
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
if (string.Equals(ParameterSetName, "ByName", StringComparison.Ordinal))
{
InfisicalPkiSubscriber subscriber = client.GetPkiSubscriber(connection, Name, resolvedProjectId);
if (subscriber != null)
{
WriteObject(subscriber);
}
return;
}
InfisicalPkiSubscriber[] all = client.ListPkiSubscribers(connection, resolvedProjectId);
foreach (InfisicalPkiSubscriber subscriber in all)
{
WriteObject(subscriber);
}
}
catch (Exception exception)
{
ThrowTerminatingForException("GetInfisicalPkiSubscriberCmdlet", "GetPkiSubscriber", exception);
}
}
}
}
@@ -6,30 +6,44 @@ using PSInfisicalAPI.Projects;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.Get, "InfisicalProject")]
[Cmdlet(VerbsCommon.Get, "InfisicalProject", DefaultParameterSetName = "List")]
[OutputType(typeof(InfisicalProject))]
public sealed class GetInfisicalProjectCmdlet : InfisicalCmdletBase
{
[Parameter(ValueFromPipelineByPropertyName = true, Position = 0)]
[Parameter(ParameterSetName = "Single", Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
[Alias("Id")]
public string ProjectId { get; set; }
[Parameter(ParameterSetName = "List")] public SwitchParameter List { get; set; }
protected override void ProcessRecord()
{
try
{
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
InfisicalProjectClient client = new InfisicalProjectClient(HttpClient, Logger);
InfisicalProject project = client.Retrieve(connection, resolvedProjectId);
if (project != null)
if (string.Equals(ParameterSetName, "Single", StringComparison.Ordinal))
{
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
InfisicalProject project = client.Retrieve(connection, resolvedProjectId);
if (project != null)
{
WriteObject(project);
}
return;
}
InfisicalProject[] projects = client.List(connection);
foreach (InfisicalProject project in projects)
{
WriteObject(project);
}
}
catch (Exception exception)
{
ThrowTerminatingForException("GetInfisicalProjectCmdlet", "RetrieveProject", exception);
ThrowTerminatingForException("GetInfisicalProjectCmdlet", "GetProject", exception);
}
}
}
@@ -1,32 +0,0 @@
using System;
using System.Management.Automation;
using PSInfisicalAPI.Connections;
using PSInfisicalAPI.Models;
using PSInfisicalAPI.Projects;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.Get, "InfisicalProjects")]
[OutputType(typeof(InfisicalProject))]
public sealed class GetInfisicalProjectsCmdlet : InfisicalCmdletBase
{
protected override void ProcessRecord()
{
try
{
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
InfisicalProjectClient client = new InfisicalProjectClient(HttpClient, Logger);
InfisicalProject[] projects = client.List(connection);
foreach (InfisicalProject project in projects)
{
WriteObject(project);
}
}
catch (Exception exception)
{
ThrowTerminatingForException("GetInfisicalProjectsCmdlet", "ListProjects", exception);
}
}
}
}
@@ -1,4 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Management.Automation;
using PSInfisicalAPI.Connections;
using PSInfisicalAPI.Models;
@@ -6,55 +8,101 @@ using PSInfisicalAPI.Secrets;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.Get, "InfisicalSecret")]
[Cmdlet(VerbsCommon.Get, "InfisicalSecret", DefaultParameterSetName = "List")]
[OutputType(typeof(InfisicalSecret))]
public sealed class GetInfisicalSecretCmdlet : InfisicalCmdletBase
{
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
[Parameter(ParameterSetName = "Single", Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
public string SecretName { get; set; }
[Parameter] public string ProjectId { get; set; }
[Parameter] public string Environment { get; set; }
[Parameter] public string SecretPath { get; set; }
[Parameter] public string ApiVersion { get; set; }
[Parameter] public int? Version { get; set; }
[Parameter] public InfisicalSecretType Type { get; set; } = InfisicalSecretType.Shared;
[Parameter] public SwitchParameter ViewSecretValue { get; set; } = SwitchParameter.Present;
[Parameter] public SwitchParameter ExpandSecretReferences { get; set; }
[Parameter] public SwitchParameter IncludeImports { get; set; }
[Parameter(ParameterSetName = "Single")] public int? Version { get; set; }
[Parameter(ParameterSetName = "Single")] public InfisicalSecretType Type { get; set; } = InfisicalSecretType.Shared;
[Parameter(ParameterSetName = "List")] public SwitchParameter List { get; set; }
[Parameter(ParameterSetName = "List")] public SwitchParameter Recursive { get; set; }
[Parameter(ParameterSetName = "List")] public SwitchParameter IncludePersonalOverrides { get; set; }
[Parameter(ParameterSetName = "List")] public Hashtable MetadataFilter { get; set; }
[Parameter(ParameterSetName = "List")] public string[] TagSlugs { get; set; }
protected override void ProcessRecord()
{
try
{
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
InfisicalSecretsClient client = new InfisicalSecretsClient(HttpClient, Logger);
InfisicalRetrieveSecretQuery query = new InfisicalRetrieveSecretQuery
if (string.Equals(ParameterSetName, "Single", StringComparison.Ordinal))
{
InfisicalRetrieveSecretQuery query = new InfisicalRetrieveSecretQuery
{
SecretName = SecretName,
ProjectId = ResolveProjectId(connection, ProjectId),
Environment = ResolveEnvironment(connection, Environment),
SecretPath = ResolveSecretPath(connection, SecretPath),
ApiVersion = ResolveApiVersion(connection, ApiVersion),
Version = Version,
Type = Type.ToString(),
ViewSecretValue = ViewSecretValue.IsPresent,
ExpandSecretReferences = ExpandSecretReferences.IsPresent,
IncludeImports = IncludeImports.IsPresent
};
InfisicalSecret secret = client.Retrieve(connection, query);
if (secret != null)
{
WriteObject(secret);
}
return;
}
InfisicalListSecretsQuery listQuery = new InfisicalListSecretsQuery
{
SecretName = SecretName,
ProjectId = ResolveProjectId(connection, ProjectId),
Environment = ResolveEnvironment(connection, Environment),
SecretPath = ResolveSecretPath(connection, SecretPath),
ApiVersion = ResolveApiVersion(connection, ApiVersion),
Version = Version,
Type = Type.ToString(),
ViewSecretValue = ViewSecretValue.IsPresent,
Recursive = Recursive.IsPresent,
IncludeImports = IncludeImports.IsPresent,
IncludePersonalOverrides = IncludePersonalOverrides.IsPresent,
ExpandSecretReferences = ExpandSecretReferences.IsPresent,
IncludeImports = IncludeImports.IsPresent
ViewSecretValue = ViewSecretValue.IsPresent,
MetadataFilter = ToStringDictionary(MetadataFilter),
TagSlugs = TagSlugs
};
InfisicalSecretsClient client = new InfisicalSecretsClient(HttpClient, Logger);
InfisicalSecret secret = client.Retrieve(connection, query);
if (secret != null)
InfisicalSecret[] secrets = client.List(connection, listQuery);
foreach (InfisicalSecret secret in secrets)
{
WriteObject(secret);
}
}
catch (Exception exception)
{
ThrowTerminatingForException("GetInfisicalSecretCmdlet", "RetrieveSecret", exception);
ThrowTerminatingForException("GetInfisicalSecretCmdlet", "GetSecret", exception);
}
}
private static Dictionary<string, string> ToStringDictionary(Hashtable hashtable)
{
if (hashtable == null) { return null; }
Dictionary<string, string> result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (DictionaryEntry entry in hashtable)
{
if (entry.Key == null) { continue; }
result[entry.Key.ToString()] = entry.Value != null ? entry.Value.ToString() : null;
}
return result;
}
}
}
@@ -1,76 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Management.Automation;
using PSInfisicalAPI.Connections;
using PSInfisicalAPI.Models;
using PSInfisicalAPI.Secrets;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.Get, "InfisicalSecrets")]
[OutputType(typeof(InfisicalSecret))]
public sealed class GetInfisicalSecretsCmdlet : InfisicalCmdletBase
{
[Parameter] public string ProjectId { get; set; }
[Parameter] public string Environment { get; set; }
[Parameter] public string SecretPath { get; set; }
[Parameter] public string ApiVersion { get; set; }
[Parameter] public SwitchParameter Recursive { get; set; }
[Parameter] public SwitchParameter IncludeImports { get; set; }
[Parameter] public SwitchParameter IncludePersonalOverrides { get; set; }
[Parameter] public SwitchParameter ExpandSecretReferences { get; set; }
[Parameter] public SwitchParameter ViewSecretValue { get; set; } = SwitchParameter.Present;
[Parameter] public Hashtable MetadataFilter { get; set; }
[Parameter] public string[] TagSlugs { get; set; }
protected override void ProcessRecord()
{
try
{
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
InfisicalListSecretsQuery query = new InfisicalListSecretsQuery
{
ProjectId = ResolveProjectId(connection, ProjectId),
Environment = ResolveEnvironment(connection, Environment),
SecretPath = ResolveSecretPath(connection, SecretPath),
ApiVersion = ResolveApiVersion(connection, ApiVersion),
Recursive = Recursive.IsPresent,
IncludeImports = IncludeImports.IsPresent,
IncludePersonalOverrides = IncludePersonalOverrides.IsPresent,
ExpandSecretReferences = ExpandSecretReferences.IsPresent,
ViewSecretValue = ViewSecretValue.IsPresent,
MetadataFilter = ToStringDictionary(MetadataFilter),
TagSlugs = TagSlugs
};
InfisicalSecretsClient client = new InfisicalSecretsClient(HttpClient, Logger);
InfisicalSecret[] secrets = client.List(connection, query);
foreach (InfisicalSecret secret in secrets)
{
WriteObject(secret);
}
}
catch (Exception exception)
{
ThrowTerminatingForException("GetInfisicalSecretsCmdlet", "RetrieveSecrets", exception);
}
}
private static Dictionary<string, string> ToStringDictionary(Hashtable hashtable)
{
if (hashtable == null) { return null; }
Dictionary<string, string> result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (DictionaryEntry entry in hashtable)
{
if (entry.Key == null) { continue; }
result[entry.Key.ToString()] = entry.Value != null ? entry.Value.ToString() : null;
}
return result;
}
}
}
@@ -6,16 +6,18 @@ using PSInfisicalAPI.Tags;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.Get, "InfisicalTag")]
[Cmdlet(VerbsCommon.Get, "InfisicalTag", DefaultParameterSetName = "List")]
[OutputType(typeof(InfisicalTag))]
public sealed class GetInfisicalTagCmdlet : InfisicalCmdletBase
{
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
[Parameter(ParameterSetName = "Single", Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
[Alias("Slug", "Id")]
public string TagSlugOrId { get; set; }
[Parameter] public string ProjectId { get; set; }
[Parameter(ParameterSetName = "List")] public SwitchParameter List { get; set; }
protected override void ProcessRecord()
{
try
@@ -23,15 +25,27 @@ namespace PSInfisicalAPI.Cmdlets
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
InfisicalTagClient client = new InfisicalTagClient(HttpClient, Logger);
InfisicalTag tag = client.Retrieve(connection, resolvedProjectId, TagSlugOrId);
if (tag != null)
if (string.Equals(ParameterSetName, "Single", StringComparison.Ordinal))
{
InfisicalTag tag = client.Retrieve(connection, resolvedProjectId, TagSlugOrId);
if (tag != null)
{
WriteObject(tag);
}
return;
}
InfisicalTag[] tags = client.List(connection, resolvedProjectId);
foreach (InfisicalTag tag in tags)
{
WriteObject(tag);
}
}
catch (Exception exception)
{
ThrowTerminatingForException("GetInfisicalTagCmdlet", "RetrieveTag", exception);
ThrowTerminatingForException("GetInfisicalTagCmdlet", "GetTag", exception);
}
}
}
@@ -1,34 +0,0 @@
using System;
using System.Management.Automation;
using PSInfisicalAPI.Connections;
using PSInfisicalAPI.Models;
using PSInfisicalAPI.Tags;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.Get, "InfisicalTags")]
[OutputType(typeof(InfisicalTag))]
public sealed class GetInfisicalTagsCmdlet : InfisicalCmdletBase
{
[Parameter] public string ProjectId { get; set; }
protected override void ProcessRecord()
{
try
{
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
InfisicalTagClient client = new InfisicalTagClient(HttpClient, Logger);
InfisicalTag[] tags = client.List(connection, resolvedProjectId);
foreach (InfisicalTag tag in tags)
{
WriteObject(tag);
}
}
catch (Exception exception)
{
ThrowTerminatingForException("GetInfisicalTagsCmdlet", "ListTags", exception);
}
}
}
}
@@ -51,5 +51,8 @@ namespace PSInfisicalAPI.Endpoints
public const string GetCertificateBundle = "GetCertificateBundle";
public const string SignCertificateBySubscriber = "SignCertificateBySubscriber";
public const string SignCertificateByCa = "SignCertificateByCa";
public const string ListPkiSubscribers = "ListPkiSubscribers";
public const string GetPkiSubscriber = "GetPkiSubscriber";
}
}
@@ -596,18 +596,7 @@ namespace PSInfisicalAPI.Endpoints
Resource = "Pki",
Version = "v1",
Method = "POST",
Template = "/api/v1/pki/pki-subscribers/{subscriberName}/sign-certificate",
RequiresAuthorization = true,
ContainsSecretMaterialInResponse = true
});
Add(map, new InfisicalEndpointDefinition
{
Name = InfisicalEndpointNames.SignCertificateBySubscriber,
Resource = "Pki",
Version = "v1",
Method = "POST",
Template = "/api/v1/cert-manager/pki-subscribers/{subscriberName}/sign-certificate",
Template = "/api/v1/pki/subscribers/{subscriberName}/sign-certificate",
RequiresAuthorization = true,
ContainsSecretMaterialInResponse = true
});
@@ -633,6 +622,26 @@ namespace PSInfisicalAPI.Endpoints
RequiresAuthorization = true,
ContainsSecretMaterialInResponse = true
});
Add(map, new InfisicalEndpointDefinition
{
Name = InfisicalEndpointNames.ListPkiSubscribers,
Resource = "Pki",
Version = "v1",
Method = "GET",
Template = "/api/v1/projects/{projectId}/pki-subscribers",
RequiresAuthorization = true
});
Add(map, new InfisicalEndpointDefinition
{
Name = InfisicalEndpointNames.GetPkiSubscriber,
Resource = "Pki",
Version = "v1",
Method = "GET",
Template = "/api/v1/pki/subscribers/{subscriberName}",
RequiresAuthorization = true
});
}
public static InfisicalEndpointDefinition Get(string name)
@@ -0,0 +1,37 @@
using System;
namespace PSInfisicalAPI.Models
{
public sealed class InfisicalPkiSubscriber
{
public string Id { get; set; }
public string ProjectId { get; set; }
public string CaId { get; set; }
public string Name { get; set; }
public string CommonName { get; set; }
public string Status { get; set; }
public string Ttl { get; set; }
public string[] SubjectAlternativeNames { get; set; }
public string[] KeyUsages { get; set; }
public string[] ExtendedKeyUsages { get; set; }
public bool? EnableAutoRenewal { get; set; }
public int? AutoRenewalPeriodInDays { get; set; }
public string LastOperationStatus { get; set; }
public string LastOperationMessage { get; set; }
public DateTimeOffset? LastOperationAtUtc { get; set; }
public DateTimeOffset? CreatedAtUtc { get; set; }
public DateTimeOffset? UpdatedAtUtc { get; set; }
public InfisicalPkiSubscriberProperties Properties { get; set; }
}
public sealed class InfisicalPkiSubscriberProperties
{
public string AzureTemplateType { get; set; }
public string Organization { get; set; }
public string OrganizationalUnit { get; set; }
public string Country { get; set; }
public string State { get; set; }
public string Locality { get; set; }
public string EmailAddress { get; set; }
}
}
@@ -270,6 +270,74 @@ namespace PSInfisicalAPI.Pki
};
}
public InfisicalPkiSubscriber[] ListPkiSubscribers(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."); }
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "projectId", resolvedProjectId } };
try
{
_logger.Information(Component, "Attempting to list Infisical PKI subscribers. Please Wait...");
InfisicalHttpResponse response = _invoker.InvokeWithCandidateFallback(connection, InfisicalEndpointNames.ListPkiSubscribers, "ListPkiSubscribers", pathParameters, null, null);
string body = response.Body;
response.Clear();
List<InfisicalPkiSubscriberResponseDto> source = ParsePkiSubscriberListBody(body);
InfisicalPkiSubscriber[] mapped = InfisicalPkiSubscriberMapper.MapMany(source, resolvedProjectId);
_logger.Information(Component, "Infisical PKI subscriber list retrieval was successful.");
return mapped;
}
catch (Exception)
{
_logger.Error(Component, "Infisical PKI subscriber list retrieval failed.");
throw;
}
}
public InfisicalPkiSubscriber GetPkiSubscriber(InfisicalConnection connection, string subscriberName, string projectId)
{
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
if (string.IsNullOrEmpty(subscriberName)) { throw new InfisicalConfigurationException("SubscriberName is required."); }
string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId);
if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); }
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "subscriberName", subscriberName } };
List<KeyValuePair<string, string>> query = new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("projectId", resolvedProjectId) };
try
{
_logger.Information(Component, string.Concat("Attempting to retrieve Infisical PKI subscriber '", subscriberName, "'. Please Wait..."));
InfisicalHttpResponse response = _invoker.InvokeWithCandidateFallback(connection, InfisicalEndpointNames.GetPkiSubscriber, "GetPkiSubscriber", pathParameters, query, null);
InfisicalPkiSubscriberResponseDto dto = _serializer.Deserialize<InfisicalPkiSubscriberResponseDto>(response.Body);
response.Clear();
InfisicalPkiSubscriber mapped = InfisicalPkiSubscriberMapper.Map(dto, resolvedProjectId);
_logger.Information(Component, "Infisical PKI subscriber retrieval was successful.");
return mapped;
}
catch (Exception)
{
_logger.Error(Component, "Infisical PKI subscriber retrieval failed.");
throw;
}
}
private List<InfisicalPkiSubscriberResponseDto> ParsePkiSubscriberListBody(string body)
{
if (string.IsNullOrEmpty(body)) { return null; }
JToken token = JToken.Parse(body);
if (token.Type == JTokenType.Array)
{
return token.ToObject<List<InfisicalPkiSubscriberResponseDto>>();
}
InfisicalPkiSubscriberListResponseDto wrapper = token.ToObject<InfisicalPkiSubscriberListResponseDto>();
return wrapper != null ? wrapper.Subscribers : null;
}
public InfisicalCertificateBundle GetCertificateBundle(InfisicalConnection connection, string serialNumber)
{
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
@@ -0,0 +1,43 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace PSInfisicalAPI.Pki
{
internal sealed class InfisicalPkiSubscriberResponseDto
{
[JsonProperty("id")] public string Id { get; set; }
[JsonProperty("projectId")] public string ProjectId { get; set; }
[JsonProperty("caId")] public string CaId { get; set; }
[JsonProperty("name")] public string Name { get; set; }
[JsonProperty("commonName")] public string CommonName { get; set; }
[JsonProperty("status")] public string Status { get; set; }
[JsonProperty("ttl")] public string Ttl { get; set; }
[JsonProperty("subjectAlternativeNames")] public List<string> SubjectAlternativeNames { get; set; }
[JsonProperty("keyUsages")] public List<string> KeyUsages { get; set; }
[JsonProperty("extendedKeyUsages")] public List<string> ExtendedKeyUsages { get; set; }
[JsonProperty("enableAutoRenewal")] public bool? EnableAutoRenewal { get; set; }
[JsonProperty("autoRenewalPeriodInDays")] public int? AutoRenewalPeriodInDays { get; set; }
[JsonProperty("lastOperationStatus")] public string LastOperationStatus { get; set; }
[JsonProperty("lastOperationMessage")] public string LastOperationMessage { get; set; }
[JsonProperty("lastOperationAt")] public string LastOperationAt { get; set; }
[JsonProperty("createdAt")] public string CreatedAt { get; set; }
[JsonProperty("updatedAt")] public string UpdatedAt { get; set; }
[JsonProperty("properties")] public InfisicalPkiSubscriberPropertiesDto Properties { get; set; }
}
internal sealed class InfisicalPkiSubscriberPropertiesDto
{
[JsonProperty("azureTemplateType")] public string AzureTemplateType { get; set; }
[JsonProperty("organization")] public string Organization { get; set; }
[JsonProperty("organizationalUnit")] public string OrganizationalUnit { get; set; }
[JsonProperty("country")] public string Country { get; set; }
[JsonProperty("state")] public string State { get; set; }
[JsonProperty("locality")] public string Locality { get; set; }
[JsonProperty("emailAddress")] public string EmailAddress { get; set; }
}
internal sealed class InfisicalPkiSubscriberListResponseDto
{
[JsonProperty("subscribers")] public List<InfisicalPkiSubscriberResponseDto> Subscribers { get; set; }
}
}
@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using PSInfisicalAPI.Models;
namespace PSInfisicalAPI.Pki
{
internal static class InfisicalPkiSubscriberMapper
{
public static InfisicalPkiSubscriber Map(InfisicalPkiSubscriberResponseDto dto, string fallbackProjectId)
{
if (dto == null)
{
return null;
}
return new InfisicalPkiSubscriber
{
Id = dto.Id,
ProjectId = !string.IsNullOrEmpty(dto.ProjectId) ? dto.ProjectId : fallbackProjectId,
CaId = dto.CaId,
Name = dto.Name,
CommonName = dto.CommonName,
Status = dto.Status,
Ttl = dto.Ttl,
SubjectAlternativeNames = dto.SubjectAlternativeNames != null ? dto.SubjectAlternativeNames.ToArray() : null,
KeyUsages = dto.KeyUsages != null ? dto.KeyUsages.ToArray() : null,
ExtendedKeyUsages = dto.ExtendedKeyUsages != null ? dto.ExtendedKeyUsages.ToArray() : null,
EnableAutoRenewal = dto.EnableAutoRenewal,
AutoRenewalPeriodInDays = dto.AutoRenewalPeriodInDays,
LastOperationStatus = dto.LastOperationStatus,
LastOperationMessage = dto.LastOperationMessage,
LastOperationAtUtc = ParseTimestamp(dto.LastOperationAt),
CreatedAtUtc = ParseTimestamp(dto.CreatedAt),
UpdatedAtUtc = ParseTimestamp(dto.UpdatedAt),
Properties = MapProperties(dto.Properties)
};
}
public static InfisicalPkiSubscriber[] MapMany(IEnumerable<InfisicalPkiSubscriberResponseDto> items, string fallbackProjectId)
{
if (items == null)
{
return Array.Empty<InfisicalPkiSubscriber>();
}
List<InfisicalPkiSubscriber> results = new List<InfisicalPkiSubscriber>();
foreach (InfisicalPkiSubscriberResponseDto dto in items)
{
InfisicalPkiSubscriber mapped = Map(dto, fallbackProjectId);
if (mapped != null)
{
results.Add(mapped);
}
}
return results.ToArray();
}
private static InfisicalPkiSubscriberProperties MapProperties(InfisicalPkiSubscriberPropertiesDto dto)
{
if (dto == null)
{
return null;
}
return new InfisicalPkiSubscriberProperties
{
AzureTemplateType = dto.AzureTemplateType,
Organization = dto.Organization,
OrganizationalUnit = dto.OrganizationalUnit,
Country = dto.Country,
State = dto.State,
Locality = dto.Locality,
EmailAddress = dto.EmailAddress
};
}
private static DateTimeOffset? ParseTimestamp(string value)
{
if (string.IsNullOrEmpty(value))
{
return null;
}
DateTimeOffset parsed;
if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out parsed))
{
return parsed;
}
return null;
}
}
}