feat(scep): rework Get-InfisicalScepMdmProfile into FromEnrollment/FromProfile/Manual parameter sets

FromEnrollment (new default) consumes an InfisicalCertificateApplicationEnrollment and auto-fills ServerUrl from scep.scepEndpointUrl, CAThumbprint from the RA certificate thumbprint, and mints a fresh dynamic challenge automatically when challengeType=dynamic and -Challenge is not supplied. FromProfile preserves the legacy projection from an InfisicalCertificateProfile but now requires -ApplicationId so the server URL is built against /scep/applications/{appId}/profiles/{profileId}/pkiclient.exe. Manual requires explicit -ServerUrl, -Challenge, and -UniqueId. Module manifest, help XML, and build.ps1 expectedCmds list updated to register the three new cmdlets. CHANGELOG updated.
This commit is contained in:
GraceSolutions
2026-06-04 19:35:16 -04:00
parent 148a09f0d9
commit 3c39a99b9a
5 changed files with 257 additions and 53 deletions
+12
View File
@@ -6,6 +6,18 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) loos
## Unreleased
## 2026.06.04.2305
- Build produced from commit 485ee8a7dd6a.
## Unreleased (carried forward)
- `Get-InfisicalCertificateApplication` added with `List` (default), `ById`, and `ByName` parameter sets. Binds to `/api/v1/cert-manager/applications` (list) and `/api/v1/cert-manager/applications/{applicationId}` / `/by-name/{name}` for single retrieval. Requests carry the `x-infisical-project-id` header so the certificate-manager scope resolves correctly. New `InfisicalCertificateApplication` model surfaces id, project, name, description, and counts.
- `Get-InfisicalCertificateApplicationEnrollment` added. Returns the API/EST/ACME/SCEP enrollment configuration for an application/profile pair (`GET /api/v1/cert-manager/applications/{applicationId}/profiles/{profileId}/enrollment`). The new `InfisicalCertificateApplicationEnrollment` model includes sub-blocks for each enrollment protocol; the SCEP block computes a SHA-1 `RaCertificateThumbprint` from the RA certificate PEM so it can be fed directly into MDM payloads.
- `New-InfisicalScepDynamicChallenge` added. Wraps `POST /scep/applications/{applicationId}/profiles/{profileId}/challenge` and returns the minted challenge as a `SecureString` (default) or string (`-AsPlainText`). The endpoint is gated by the dynamic-challenge feature on the target Infisical instance and by the calling identity's permission on `certificate-application-enrollment`.
- `Get-InfisicalScepMdmProfile` reworked into three parameter sets. `FromEnrollment` (new default) consumes an `InfisicalCertificateApplicationEnrollment` and auto-resolves `ServerUrl` from `scep.scepEndpointUrl`, `CAThumbprint` from the RA certificate, and the SCEP challenge (auto-minting when `challengeType=dynamic` and `-Challenge` is not supplied). `FromProfile` keeps the legacy projection from an `InfisicalCertificateProfile`, now requires `-ApplicationId`, and the default server URL is built against `/scep/applications/{appId}/profiles/{profileId}/pkiclient.exe`. `Manual` requires explicit `-ServerUrl`, `-Challenge`, and `-UniqueId`.
- `InfisicalApiInvoker` accepts an optional `extraHeaders` argument so callers can attach the `x-infisical-project-id` header and override `Accept` for plain-text responses (used by the new SCEP challenge endpoint).
## 2026.06.04.2147
- Build produced from commit 183fb48c32ce.
+5 -2
View File
@@ -1,6 +1,6 @@
@{
RootModule = 'PSInfisicalAPI.psm1'
ModuleVersion = '2026.06.04.2147'
ModuleVersion = '2026.06.04.2305'
GUID = 'b8a2f3d4-7c51-4d2f-9e6a-1f0c8b3d4e51'
Author = 'Grace Solutions'
CompanyName = 'Grace Solutions'
@@ -46,6 +46,9 @@
'Install-InfisicalCertificate',
'Uninstall-InfisicalCertificate',
'Export-InfisicalCertificate',
'Get-InfisicalCertificateApplication',
'Get-InfisicalCertificateApplicationEnrollment',
'New-InfisicalScepDynamicChallenge',
'Get-InfisicalScepMdmProfile',
'Export-InfisicalScepMdmProfile',
'Write-InfisicalScepMdmProfileToWmi'
@@ -60,7 +63,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 = '183fb48c32ce'
CommitHash = '485ee8a7dd6a'
}
}
}
@@ -1477,33 +1477,130 @@ $UninstallInfisicalCertificateResult = Uninstall-InfisicalCertificate @Uninstall
</command:examples>
</command:command>
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
<command:details>
<command:name>Get-InfisicalCertificateApplication</command:name>
<maml:description><maml:para>Lists or retrieves an Infisical Certificate Manager Application from the active project.</maml:para></maml:description>
<command:verb>Get</command:verb>
<command:noun>InfisicalCertificateApplication</command:noun>
</command:details>
<maml:description>
<maml:para>Reads Infisical certificate-manager Applications (the join target used by EST/ACME/SCEP profile attachments) using the active connection's project scope. The List parameter set returns all applications visible to the caller; the ById and ByName sets return a single application. ProjectId falls back to the active connection when omitted.</maml:para>
</maml:description>
<command:examples>
<command:example>
<maml:title>EXAMPLE 1</maml:title>
<dev:code>Get-InfisicalCertificateApplication</dev:code>
<dev:remarks><maml:para>Lists certificate-manager applications for the active project.</maml:para></dev:remarks>
</command:example>
<command:example>
<maml:title>EXAMPLE 2</maml:title>
<dev:code>Get-InfisicalCertificateApplication -ApplicationName 'workstation-mdm'</dev:code>
<dev:remarks><maml:para>Retrieves a single application by name.</maml:para></dev:remarks>
</command:example>
<command:example>
<maml:title>EXAMPLE 3</maml:title>
<dev:code>$GetInfisicalCertificateApplicationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
$GetInfisicalCertificateApplicationParameters.Id = $ApplicationId
$GetInfisicalCertificateApplicationParameters.ProjectId = $ProjectId
$GetInfisicalCertificateApplicationParameters.Verbose = $True
$GetInfisicalCertificateApplicationResult = Get-InfisicalCertificateApplication @GetInfisicalCertificateApplicationParameters</dev:code>
<dev:remarks><maml:para>Retrieves a single application by id from an explicit project.</maml:para></dev:remarks>
</command:example>
</command:examples>
</command:command>
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
<command:details>
<command:name>Get-InfisicalCertificateApplicationEnrollment</command:name>
<maml:description><maml:para>Retrieves the API/EST/ACME/SCEP enrollment configuration attached to an application/profile pair.</maml:para></maml:description>
<command:verb>Get</command:verb>
<command:noun>InfisicalCertificateApplicationEnrollment</command:noun>
</command:details>
<maml:description>
<maml:para>Returns the InfisicalCertificateApplicationEnrollment for the given application and certificate profile, including any configured SCEP sub-block (server URL, RA certificate PEM, computed SHA-1 RaCertificateThumbprint, challenge type, and challenge endpoint URL when dynamic).</maml:para>
</maml:description>
<command:examples>
<command:example>
<maml:title>EXAMPLE 1</maml:title>
<dev:code>Get-InfisicalCertificateApplicationEnrollment -ApplicationId $AppId -ProfileId $ProfileId</dev:code>
<dev:remarks><maml:para>Fetches the enrollment configuration for an application/profile pair.</maml:para></dev:remarks>
</command:example>
<command:example>
<maml:title>EXAMPLE 2</maml:title>
<dev:code>$GetInfisicalCertificateApplicationEnrollmentParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
$GetInfisicalCertificateApplicationEnrollmentParameters.ApplicationId = $ApplicationId
$GetInfisicalCertificateApplicationEnrollmentParameters.ProfileId = $ProfileId
$GetInfisicalCertificateApplicationEnrollmentParameters.Verbose = $True
$GetInfisicalCertificateApplicationEnrollmentResult = Get-InfisicalCertificateApplicationEnrollment @GetInfisicalCertificateApplicationEnrollmentParameters</dev:code>
<dev:remarks><maml:para>Retrieves the enrollment configuration and feeds it downstream to Get-InfisicalScepMdmProfile.</maml:para></dev:remarks>
</command:example>
</command:examples>
</command:command>
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
<command:details>
<command:name>New-InfisicalScepDynamicChallenge</command:name>
<maml:description><maml:para>Generates a one-time SCEP challenge from an application/profile that is configured with dynamic challenge mode.</maml:para></maml:description>
<command:verb>New</command:verb>
<command:noun>InfisicalScepDynamicChallenge</command:noun>
</command:details>
<maml:description>
<maml:para>POSTs to /scep/applications/{applicationId}/profiles/{profileId}/challenge and returns the minted challenge as a SecureString. Use -AsPlainText to return a string instead. Requires the active machine identity to have read access on certificate-application-enrollment, and the target SCEP profile must be set to challengeType=dynamic. Dynamic challenges are an Enterprise-tier feature on managed Infisical deployments.</maml:para>
</maml:description>
<command:examples>
<command:example>
<maml:title>EXAMPLE 1</maml:title>
<dev:code>$Challenge = New-InfisicalScepDynamicChallenge -ApplicationId $AppId -ProfileId $ProfileId</dev:code>
<dev:remarks><maml:para>Mints a single-use SCEP challenge and stores it as a SecureString.</maml:para></dev:remarks>
</command:example>
<command:example>
<maml:title>EXAMPLE 2</maml:title>
<dev:code>$NewInfisicalScepDynamicChallengeParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
$NewInfisicalScepDynamicChallengeParameters.ApplicationId = $ApplicationId
$NewInfisicalScepDynamicChallengeParameters.ProfileId = $ProfileId
$NewInfisicalScepDynamicChallengeParameters.AsPlainText = $True
$NewInfisicalScepDynamicChallengeParameters.Verbose = $True
$NewInfisicalScepDynamicChallengeResult = New-InfisicalScepDynamicChallenge @NewInfisicalScepDynamicChallengeParameters</dev:code>
<dev:remarks><maml:para>Mints a plain-text challenge for use in environments where SecureString is inconvenient.</maml:para></dev:remarks>
</command:example>
</command:examples>
</command:command>
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
<command:details>
<command:name>Get-InfisicalScepMdmProfile</command:name>
<maml:description><maml:para>Builds an Infisical SCEP MDM profile model from a certificate profile, suitable for SyncML export or local MDM enrollment.</maml:para></maml:description>
<maml:description><maml:para>Builds an Infisical SCEP MDM profile model from an application enrollment, certificate profile, or fully manual inputs.</maml:para></maml:description>
<command:verb>Get</command:verb>
<command:noun>InfisicalScepMdmProfile</command:noun>
</command:details>
<maml:description>
<maml:para>Projects an InfisicalCertificateProfile (pipeline-bound) into an InfisicalScepMdmProfile that mirrors the Windows ClientCertificateInstall/SCEP CSP node set. -Challenge is accepted as a SecureString and decrypted into the model only at write-time. -ServerUrl defaults to {baseUri}/scep/{profileId}/pkiclient.exe derived from the active connection. -UniqueId defaults to a sanitized form of the source profile slug. KeyAlgorithm and EkuMapping are inherited from the source profile defaults unless overridden.</maml:para>
<maml:para>Produces an InfisicalScepMdmProfile that mirrors the Windows ClientCertificateInstall/SCEP CSP node set. FromEnrollment (default) consumes an InfisicalCertificateApplicationEnrollment and auto-fills ServerUrl from scep.scepEndpointUrl and CAThumbprint from the RA certificate; if the enrollment is configured for dynamic challenge mode, a fresh challenge is minted automatically when -Challenge is not supplied. FromProfile keeps the legacy projection from an InfisicalCertificateProfile and now requires -ApplicationId so the server URL can be built against /scep/applications/{appId}/profiles/{profileId}/pkiclient.exe. Manual requires explicit -ServerUrl, -Challenge, and -UniqueId.</maml:para>
</maml:description>
<maml:alertSet>
<maml:title>Notes</maml:title>
<maml:alert>
<maml:para>The SCEP endpoint URL ends in 'pkiclient.exe' for RFC 8894 / Cisco SCEP client compatibility. The source profile must have SCEP enrollment enabled on the server side for enrollment to succeed; this cmdlet does not validate that.</maml:para>
<maml:para>The SCEP endpoint URL ends in 'pkiclient.exe' for RFC 8894 / Cisco SCEP client compatibility. SecureString -Challenge is decrypted into the model only at write-time.</maml:para>
</maml:alert>
</maml:alertSet>
<command:examples>
<command:example>
<maml:title>EXAMPLE 1</maml:title>
<dev:code>Get-InfisicalCertificateProfile -CertificateProfileId $ProfileId | Get-InfisicalScepMdmProfile -Challenge (Read-Host -AsSecureString 'SCEP challenge')</dev:code>
<dev:remarks><maml:para>Builds a default SCEP MDM profile with the server URL inferred from the active connection.</maml:para></dev:remarks>
<dev:code>Get-InfisicalCertificateApplicationEnrollment -ApplicationId $AppId -ProfileId $ProfileId | Get-InfisicalScepMdmProfile</dev:code>
<dev:remarks><maml:para>Builds a SCEP MDM profile from an enrollment, auto-resolving ServerUrl, CAThumbprint, and (for dynamic mode) the challenge.</maml:para></dev:remarks>
</command:example>
<command:example>
<maml:title>EXAMPLE 2</maml:title>
<dev:code>Get-InfisicalCertificateProfile -CertificateProfileId $ProfileId | Get-InfisicalScepMdmProfile -ApplicationId $AppId -Challenge (Read-Host -AsSecureString 'SCEP challenge')</dev:code>
<dev:remarks><maml:para>Builds a profile from a certificate profile (legacy path) with an explicit application id and static challenge.</maml:para></dev:remarks>
</command:example>
<command:example>
<maml:title>EXAMPLE 3</maml:title>
<dev:code>$GetInfisicalScepMdmProfileParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
$GetInfisicalScepMdmProfileParameters.InputObject = (Get-InfisicalCertificateProfile -CertificateProfileId $ProfileId)
$GetInfisicalScepMdmProfileParameters.Challenge = (Read-Host -AsSecureString 'SCEP challenge')
$GetInfisicalScepMdmProfileParameters.EnrollmentObject = $Enrollment
$GetInfisicalScepMdmProfileParameters.UniqueId = 'WindowsClientAuth'
$GetInfisicalScepMdmProfileParameters.Scope = 'Device'
$GetInfisicalScepMdmProfileParameters.SubjectName = "CN=$($env:COMPUTERNAME)"
@@ -1514,7 +1611,7 @@ $GetInfisicalScepMdmProfileParameters.ValidPeriodUnits = 1
$GetInfisicalScepMdmProfileParameters.Verbose = $True
$GetInfisicalScepMdmProfileResult = Get-InfisicalScepMdmProfile @GetInfisicalScepMdmProfileParameters</dev:code>
<dev:remarks><maml:para>Builds a device-scope SCEP MDM profile with explicit subject and key parameters for downstream export or local enrollment.</maml:para></dev:remarks>
<dev:remarks><maml:para>Builds a device-scope SCEP MDM profile from an enrollment with overridden subject and key parameters.</maml:para></dev:remarks>
</command:example>
</command:examples>
</command:command>
+4 -1
View File
@@ -140,6 +140,9 @@ function Write-Manifest {
'Install-InfisicalCertificate',
'Uninstall-InfisicalCertificate',
'Export-InfisicalCertificate',
'Get-InfisicalCertificateApplication',
'Get-InfisicalCertificateApplicationEnrollment',
'New-InfisicalScepDynamicChallenge',
'Get-InfisicalScepMdmProfile',
'Export-InfisicalScepMdmProfile',
'Write-InfisicalScepMdmProfileToWmi'
@@ -207,7 +210,7 @@ if (`$cmds.Count -eq 0) {
throw "No cmdlets were exported by the PSInfisicalAPI module."
}
`$expectedCmds = @('Connect-Infisical','Disconnect-Infisical','Get-InfisicalSecret','New-InfisicalSecret','Update-InfisicalSecret','Remove-InfisicalSecret','Copy-InfisicalSecret','ConvertTo-InfisicalSecretDictionary','Export-InfisicalSecrets','Get-InfisicalProject','New-InfisicalProject','Update-InfisicalProject','Remove-InfisicalProject','Get-InfisicalEnvironment','New-InfisicalEnvironment','Update-InfisicalEnvironment','Remove-InfisicalEnvironment','Get-InfisicalFolder','New-InfisicalFolder','Update-InfisicalFolder','Remove-InfisicalFolder','Get-InfisicalTag','New-InfisicalTag','Update-InfisicalTag','Remove-InfisicalTag','Get-InfisicalCertificateAuthority','Get-InfisicalPkiSubscriber','Get-InfisicalCertificateProfile','Get-InfisicalCertificatePolicy','Get-InfisicalCertificate','Search-InfisicalCertificate','Request-InfisicalCertificate','ConvertTo-InfisicalCertificate','Install-InfisicalCertificate','Uninstall-InfisicalCertificate','Export-InfisicalCertificate','Get-InfisicalScepMdmProfile','Export-InfisicalScepMdmProfile','Write-InfisicalScepMdmProfileToWmi')
`$expectedCmds = @('Connect-Infisical','Disconnect-Infisical','Get-InfisicalSecret','New-InfisicalSecret','Update-InfisicalSecret','Remove-InfisicalSecret','Copy-InfisicalSecret','ConvertTo-InfisicalSecretDictionary','Export-InfisicalSecrets','Get-InfisicalProject','New-InfisicalProject','Update-InfisicalProject','Remove-InfisicalProject','Get-InfisicalEnvironment','New-InfisicalEnvironment','Update-InfisicalEnvironment','Remove-InfisicalEnvironment','Get-InfisicalFolder','New-InfisicalFolder','Update-InfisicalFolder','Remove-InfisicalFolder','Get-InfisicalTag','New-InfisicalTag','Update-InfisicalTag','Remove-InfisicalTag','Get-InfisicalCertificateAuthority','Get-InfisicalPkiSubscriber','Get-InfisicalCertificateProfile','Get-InfisicalCertificatePolicy','Get-InfisicalCertificate','Search-InfisicalCertificate','Request-InfisicalCertificate','ConvertTo-InfisicalCertificate','Install-InfisicalCertificate','Uninstall-InfisicalCertificate','Export-InfisicalCertificate','Get-InfisicalCertificateApplication','Get-InfisicalCertificateApplicationEnrollment','New-InfisicalScepDynamicChallenge','Get-InfisicalScepMdmProfile','Export-InfisicalScepMdmProfile','Write-InfisicalScepMdmProfileToWmi')
foreach (`$expected in `$expectedCmds) {
if (-not (Get-Command -Name `$expected -Module PSInfisicalAPI -ErrorAction SilentlyContinue)) {
throw "Cmdlet not found: `$expected"
@@ -6,24 +6,39 @@ using System.Runtime.InteropServices;
using System.Security;
using PSInfisicalAPI.Connections;
using PSInfisicalAPI.Models;
using PSInfisicalAPI.Pki;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.Get, "InfisicalScepMdmProfile")]
[Cmdlet(VerbsCommon.Get, "InfisicalScepMdmProfile", DefaultParameterSetName = "FromEnrollment")]
[OutputType(typeof(InfisicalScepMdmProfile))]
public sealed class GetInfisicalScepMdmProfileCmdlet : InfisicalCmdletBase
{
private const string Component = "GetInfisicalScepMdmProfileCmdlet";
[Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)]
[Parameter(ParameterSetName = "FromEnrollment", Mandatory = true, ValueFromPipeline = true, Position = 0)]
[Alias("Enrollment")]
public InfisicalCertificateApplicationEnrollment EnrollmentObject { get; set; }
[Parameter(ParameterSetName = "FromProfile", Mandatory = true, ValueFromPipeline = true, Position = 0)]
[Alias("Profile", "CertificateProfile")]
public InfisicalCertificateProfile InputObject { get; set; }
[Parameter(Mandatory = true)]
[Parameter(ParameterSetName = "FromProfile", Mandatory = true)]
[Alias("AppId")]
public string ApplicationId { get; set; }
[Parameter(ParameterSetName = "FromEnrollment")]
[Parameter(ParameterSetName = "FromProfile")]
[Parameter(ParameterSetName = "Manual", Mandatory = true)]
public SecureString Challenge { get; set; }
[Parameter(ParameterSetName = "Manual", Mandatory = true)]
[Parameter(ParameterSetName = "FromProfile")]
[Parameter(ParameterSetName = "FromEnrollment")]
public string ServerUrl { get; set; }
[Parameter] public string UniqueId { get; set; }
[Parameter] public string ServerUrl { get; set; }
[Parameter]
[ValidateSet("Device", "User")]
@@ -52,30 +67,92 @@ namespace PSInfisicalAPI.Cmdlets
protected override void ProcessRecord()
{
try
{
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
if (string.Equals(ParameterSetName, "FromEnrollment", StringComparison.Ordinal))
{
WriteObject(BuildFromEnrollment(connection));
return;
}
if (string.Equals(ParameterSetName, "FromProfile", StringComparison.Ordinal))
{
WriteObject(BuildFromProfile(connection));
return;
}
WriteObject(BuildManual(connection));
}
catch (Exception exception)
{
ThrowTerminatingForException(Component, "GetScepMdmProfile", exception);
}
}
private InfisicalScepMdmProfile BuildFromEnrollment(InfisicalConnection connection)
{
if (EnrollmentObject == null) { throw new InvalidOperationException("EnrollmentObject is required."); }
if (string.IsNullOrEmpty(EnrollmentObject.ApplicationId)) { throw new InvalidOperationException("EnrollmentObject.ApplicationId is required."); }
if (string.IsNullOrEmpty(EnrollmentObject.ProfileId)) { throw new InvalidOperationException("EnrollmentObject.ProfileId is required."); }
InfisicalCertificateApplicationScepEnrollment scep = EnrollmentObject.Scep;
if (scep == null) { throw new InvalidOperationException("Enrollment does not have SCEP configured."); }
string resolvedServerUrl = FirstNonEmpty(ServerUrl, scep.ScepEndpointUrl, BuildDefaultServerUrl(connection, EnrollmentObject.ApplicationId, EnrollmentObject.ProfileId));
string resolvedUniqueId = !string.IsNullOrEmpty(UniqueId) ? UniqueId : SanitizeForCspId(EnrollmentObject.ProfileId);
string resolvedThumbprint = !string.IsNullOrEmpty(CAThumbprint) ? CAThumbprint : scep.RaCertificateThumbprint;
string resolvedChallenge = ResolveChallengeFromEnrollment(connection, scep);
InfisicalScepMdmProfile result = NewProfileShell(resolvedUniqueId, resolvedServerUrl, resolvedChallenge, resolvedThumbprint, null, null);
result.SourceProfileId = EnrollmentObject.ProfileId;
Logger.Verbose(Component, string.Concat("Built SCEP MDM profile from enrollment for application '", EnrollmentObject.ApplicationId, "' / profile '", EnrollmentObject.ProfileId, "' targeting ", result.ServerUrl, " (UniqueId=", result.UniqueId, ", Scope=", result.Scope, ", ChallengeType=", scep.ChallengeType ?? "<unknown>", ")."));
return result;
}
private InfisicalScepMdmProfile BuildFromProfile(InfisicalConnection connection)
{
if (InputObject == null) { throw new InvalidOperationException("InputObject is required."); }
if (string.IsNullOrEmpty(InputObject.Id)) { throw new InvalidOperationException("InputObject.Id is required."); }
if (string.IsNullOrEmpty(ApplicationId)) { throw new InvalidOperationException("ApplicationId is required when binding by certificate profile."); }
if (Challenge == null) { throw new InvalidOperationException("Challenge is required when building from a certificate profile."); }
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
string resolvedServerUrl = !string.IsNullOrEmpty(ServerUrl) ? ServerUrl : BuildDefaultServerUrl(connection, InputObject.Id);
string resolvedServerUrl = !string.IsNullOrEmpty(ServerUrl) ? ServerUrl : BuildDefaultServerUrl(connection, ApplicationId, InputObject.Id);
string resolvedUniqueId = !string.IsNullOrEmpty(UniqueId) ? UniqueId : SanitizeForCspId(!string.IsNullOrEmpty(InputObject.Slug) ? InputObject.Slug : InputObject.Id);
InfisicalCertificateProfileDefaults defaults = InputObject.Defaults;
string resolvedKeyAlgorithm = !string.IsNullOrEmpty(KeyAlgorithm) ? KeyAlgorithm : MapKeyAlgorithm(defaults != null ? defaults.KeyAlgorithm : null);
string resolvedEku = !string.IsNullOrEmpty(EkuMapping) ? EkuMapping : JoinEkuOids(defaults != null ? defaults.ExtendedKeyUsages : null);
InfisicalScepMdmProfile result = new InfisicalScepMdmProfile
InfisicalScepMdmProfile result = NewProfileShell(resolvedUniqueId, resolvedServerUrl, SecureStringToPlainText(Challenge), CAThumbprint, resolvedKeyAlgorithm, resolvedEku);
result.SourceProfileId = InputObject.Id;
result.SourceProfileSlug = InputObject.Slug;
Logger.Verbose(Component, string.Concat("Built SCEP MDM profile for source profile '", InputObject.Slug ?? InputObject.Id, "' targeting ", result.ServerUrl, " (UniqueId=", result.UniqueId, ", Scope=", result.Scope, ")."));
return result;
}
private InfisicalScepMdmProfile BuildManual(InfisicalConnection connection)
{
UniqueId = resolvedUniqueId,
if (string.IsNullOrEmpty(UniqueId)) { throw new InvalidOperationException("UniqueId is required in Manual mode."); }
string resolvedChallenge = SecureStringToPlainText(Challenge);
InfisicalScepMdmProfile result = NewProfileShell(UniqueId, ServerUrl, resolvedChallenge, CAThumbprint, KeyAlgorithm, EkuMapping);
Logger.Verbose(Component, string.Concat("Built SCEP MDM profile in Manual mode targeting ", result.ServerUrl, " (UniqueId=", result.UniqueId, ", Scope=", result.Scope, ")."));
return result;
}
private InfisicalScepMdmProfile NewProfileShell(string uniqueId, string serverUrl, string challenge, string thumbprint, string keyAlgorithm, string ekuMapping)
{
return new InfisicalScepMdmProfile
{
UniqueId = uniqueId,
Scope = Scope,
ServerUrl = resolvedServerUrl,
Challenge = SecureStringToPlainText(Challenge),
ServerUrl = serverUrl,
Challenge = challenge,
SubjectName = SubjectName,
SubjectAlternativeNames = SubjectAlternativeNames,
EkuMapping = resolvedEku,
EkuMapping = ekuMapping,
KeyUsage = KeyUsage,
KeyLength = KeyLength,
KeyAlgorithm = resolvedKeyAlgorithm,
KeyAlgorithm = keyAlgorithm,
HashAlgorithm = HashAlgorithm,
KeyProtection = KeyProtection,
ContainerName = ContainerName,
@@ -84,26 +161,38 @@ namespace PSInfisicalAPI.Cmdlets
RetryCount = RetryCount,
RetryDelay = RetryDelay,
TemplateName = TemplateName,
CAThumbprint = CAThumbprint,
CustomTextToShowInPrompt = CustomTextToShowInPrompt,
SourceProfileId = InputObject.Id,
SourceProfileSlug = InputObject.Slug
CAThumbprint = thumbprint,
CustomTextToShowInPrompt = CustomTextToShowInPrompt
};
Logger.Verbose(Component, string.Concat("Built SCEP MDM profile for source profile '", InputObject.Slug ?? InputObject.Id, "' targeting ", result.ServerUrl, " (UniqueId=", result.UniqueId, ", Scope=", result.Scope, ")."));
WriteObject(result);
}
catch (Exception exception)
private string ResolveChallengeFromEnrollment(InfisicalConnection connection, InfisicalCertificateApplicationScepEnrollment scep)
{
ThrowTerminatingForException(Component, "GetScepMdmProfile", exception);
}
if (Challenge != null) { return SecureStringToPlainText(Challenge); }
string challengeType = scep.ChallengeType ?? string.Empty;
if (string.Equals(challengeType, "dynamic", StringComparison.OrdinalIgnoreCase))
{
InfisicalPkiClient client = new InfisicalPkiClient(HttpClient, Logger);
Logger.Verbose(Component, "Minting SCEP dynamic challenge for enrollment.");
return client.GenerateScepDynamicChallenge(connection, EnrollmentObject.ApplicationId, EnrollmentObject.ProfileId);
}
private static string BuildDefaultServerUrl(InfisicalConnection connection, string profileId)
throw new InvalidOperationException(string.Concat("Enrollment uses challengeType '", challengeType, "'. Supply -Challenge with the configured static challenge password."));
}
private static string BuildDefaultServerUrl(InfisicalConnection connection, string applicationId, string profileId)
{
if (connection == null || connection.BaseUri == null) { throw new InvalidOperationException("Active Infisical connection is required to derive ServerUrl."); }
string baseUrl = connection.BaseUri.GetLeftPart(UriPartial.Authority);
return string.Concat(baseUrl, "/scep/", profileId, "/pkiclient.exe");
return string.Concat(baseUrl, "/scep/applications/", applicationId, "/profiles/", profileId, "/pkiclient.exe");
}
private static string FirstNonEmpty(params string[] values)
{
if (values == null) { return null; }
foreach (string value in values) { if (!string.IsNullOrEmpty(value)) { return value; } }
return null;
}
private static string SanitizeForCspId(string input)