diff --git a/CHANGELOG.md b/CHANGELOG.md index 27598d3..db4ee9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,15 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) loos ## Unreleased +- `Get-InfisicalScepMdmProfile` added. Projects an `InfisicalCertificateProfile` (pipeline-bound) into a new `InfisicalScepMdmProfile` model that mirrors the Windows `ClientCertificateInstall/SCEP` CSP node set. `-ServerUrl` defaults to `{baseUri}/scep/{profileId}/pkiclient.exe` derived from the active connection (the `pkiclient.exe` suffix is the RFC 8894 / Cisco SCEP client compatibility holdover, not a server-side executable). `-UniqueId` defaults to a sanitized slug. `-Challenge` is a `SecureString` decrypted only when materializing the model. `KeyAlgorithm` and `EkuMapping` are inherited from the source profile defaults unless overridden. +- `Export-InfisicalScepMdmProfile` added. Serializes the model via `InfisicalScepMdmProfile.ToSyncMl()` (XDocument build, XmlWriter emit, XmlReader round-trip validation) and writes the result to `-Path` as UTF-8 without BOM. Auto-creates the target directory, honors `-WhatIf`/`-Confirm`, and follows the project rule for `-Force`: if the destination exists without `-Force`, the cmdlet logs a warning and returns instead of throwing. `-PassThru` emits the resulting `FileInfo`. +- `Write-InfisicalScepMdmProfileToWmi` added. Submits the same model to the local MDM Bridge WMI provider by invoking `New-CimInstance -Namespace root/cimv2/mdm/dmmap -ClassName MDM_ClientCertificateInstall_SCEP02 -Property ` through the host runspace (no new package references). Guards: throws `PlatformNotSupportedException` off Windows; device-scope enrollment requires an elevated session unless `-SkipElevationCheck` is passed; supports `-WhatIf`/`-Confirm`; `-PassThru` emits the returned CIM instance. Override `-ClassName` when targeting a different SCEP CSP version on the host. + ## 2026.06.04.2112 - Build produced from commit 3754de74f6c8. -## Unreleased (carried forward) +## Unreleased (carried forward) - 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. diff --git a/Module/PSInfisicalAPI/PSInfisicalAPI.psd1 b/Module/PSInfisicalAPI/PSInfisicalAPI.psd1 index 1095688..89e2f80 100644 --- a/Module/PSInfisicalAPI/PSInfisicalAPI.psd1 +++ b/Module/PSInfisicalAPI/PSInfisicalAPI.psd1 @@ -45,7 +45,10 @@ 'ConvertTo-InfisicalCertificate', 'Install-InfisicalCertificate', 'Uninstall-InfisicalCertificate', - 'Export-InfisicalCertificate' + 'Export-InfisicalCertificate', + 'Get-InfisicalScepMdmProfile', + 'Export-InfisicalScepMdmProfile', + 'Write-InfisicalScepMdmProfileToWmi' ) AliasesToExport = @() VariablesToExport = @() diff --git a/Module/PSInfisicalAPI/en-US/PSInfisicalAPI.dll-Help.xml b/Module/PSInfisicalAPI/en-US/PSInfisicalAPI.dll-Help.xml index 799d13e..ffdcffb 100644 --- a/Module/PSInfisicalAPI/en-US/PSInfisicalAPI.dll-Help.xml +++ b/Module/PSInfisicalAPI/en-US/PSInfisicalAPI.dll-Help.xml @@ -1477,6 +1477,121 @@ $UninstallInfisicalCertificateResult = Uninstall-InfisicalCertificate @Uninstall + + + Get-InfisicalScepMdmProfile + Builds an Infisical SCEP MDM profile model from a certificate profile, suitable for SyncML export or local MDM enrollment. + Get + InfisicalScepMdmProfile + + + 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. + + + Notes + + 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. + + + + + EXAMPLE 1 + Get-InfisicalCertificateProfile -CertificateProfileId $ProfileId | Get-InfisicalScepMdmProfile -Challenge (Read-Host -AsSecureString 'SCEP challenge') + Builds a default SCEP MDM profile with the server URL inferred from the active connection. + + + EXAMPLE 2 + $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.UniqueId = 'WindowsClientAuth' +$GetInfisicalScepMdmProfileParameters.Scope = 'Device' +$GetInfisicalScepMdmProfileParameters.SubjectName = "CN=$($env:COMPUTERNAME)" +$GetInfisicalScepMdmProfileParameters.KeyLength = 2048 +$GetInfisicalScepMdmProfileParameters.HashAlgorithm = 'SHA256' +$GetInfisicalScepMdmProfileParameters.ValidPeriod = 'Years' +$GetInfisicalScepMdmProfileParameters.ValidPeriodUnits = 1 +$GetInfisicalScepMdmProfileParameters.Verbose = $True +$GetInfisicalScepMdmProfileResult = Get-InfisicalScepMdmProfile @GetInfisicalScepMdmProfileParameters + Builds a device-scope SCEP MDM profile with explicit subject and key parameters for downstream export or local enrollment. + + + + + + + Export-InfisicalScepMdmProfile + Writes an InfisicalScepMdmProfile to disk as a SyncML payload suitable for MDM delivery. + Export + InfisicalScepMdmProfile + + + Serializes the supplied InfisicalScepMdmProfile via ToSyncMl() and writes the result to -Path as UTF-8 (no BOM). Auto-creates the target directory. If the file exists and -Force is not specified the cmdlet logs a warning and returns instead of throwing. Honors -WhatIf and -Confirm. -PassThru emits the resulting FileInfo. + + + Notes + + The generated SyncML is round-trip-validated through XmlReader before being written. Pair with Write-InfisicalScepMdmProfileToWmi to apply the same model to the local MDM Bridge instead of exporting to a file. + + + + + EXAMPLE 1 + $Profile | Export-InfisicalScepMdmProfile -Path 'C:\Temp\scep.syncml' -Force + Writes the SyncML payload for the supplied SCEP MDM profile, overwriting any existing file. + + + EXAMPLE 2 + $ExportInfisicalScepMdmProfileParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase) +$ExportInfisicalScepMdmProfileParameters.InputObject = $Profile +$ExportInfisicalScepMdmProfileParameters.Path = "C:\ProgramData\Infisical\scep-$($Profile.UniqueId).syncml" +$ExportInfisicalScepMdmProfileParameters.Force = $True +$ExportInfisicalScepMdmProfileParameters.PassThru = $True +$ExportInfisicalScepMdmProfileParameters.Verbose = $True + +$ExportInfisicalScepMdmProfileResult = Export-InfisicalScepMdmProfile @ExportInfisicalScepMdmProfileParameters + Writes the SyncML payload to a per-profile path under ProgramData and returns the resulting FileInfo. + + + + + + + Write-InfisicalScepMdmProfileToWmi + Submits an InfisicalScepMdmProfile to the local Windows MDM Bridge WMI provider to trigger SCEP enrollment. + Write + InfisicalScepMdmProfileToWmi + + + Creates a new CIM instance under the MDM Bridge namespace (default: root/cimv2/mdm/dmmap, class MDM_ClientCertificateInstall_SCEP02) by invoking New-CimInstance through the host runspace. Honors -WhatIf and -Confirm. -PassThru emits the resulting CIM instance. Throws PlatformNotSupportedException off Windows. Device-scope enrollment requires an elevated session; pass -SkipElevationCheck to bypass the guard. + + + Notes + + The MDM Bridge WMI provider runs the enrollment asynchronously; success here means the enrollment was submitted, not that a certificate has been issued. Inspect the corresponding ClientCertificateInstall/SCEP/<UniqueId>/Install nodes for status. Override -ClassName when targeting a different SCEP CSP version on the host. + + + + + EXAMPLE 1 + $Profile | Write-InfisicalScepMdmProfileToWmi -PassThru + Submits the SCEP MDM profile to the local MDM Bridge and emits the created CIM instance. + + + EXAMPLE 2 + $WriteInfisicalScepMdmProfileToWmiParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase) +$WriteInfisicalScepMdmProfileToWmiParameters.InputObject = $Profile +$WriteInfisicalScepMdmProfileToWmiParameters.Namespace = 'root/cimv2/mdm/dmmap' +$WriteInfisicalScepMdmProfileToWmiParameters.ClassName = 'MDM_ClientCertificateInstall_SCEP02' +$WriteInfisicalScepMdmProfileToWmiParameters.SkipElevationCheck = $False +$WriteInfisicalScepMdmProfileToWmiParameters.PassThru = $True +$WriteInfisicalScepMdmProfileToWmiParameters.Verbose = $True + +$WriteInfisicalScepMdmProfileToWmiResult = Write-InfisicalScepMdmProfileToWmi @WriteInfisicalScepMdmProfileToWmiParameters + Submits a device-scope SCEP enrollment through the MDM Bridge and returns the CIM instance for downstream inspection. + + + diff --git a/README.md b/README.md index 9ce60c7..f155e8c 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Import-Module -Name .\Module\PSInfisicalAPI ## Cmdlets -The module exports 34 cmdlets. Discovery cmdlets (`Get-Infisical*`) use a `List` (default) / single-record parameter-set pair: invoking without the identity parameter returns the collection, supplying the identity parameter returns one record. +The module exports 37 cmdlets. Discovery cmdlets (`Get-Infisical*`) use a `List` (default) / single-record parameter-set pair: invoking without the identity parameter returns the collection, supplying the identity parameter returns one record. ### Session @@ -96,6 +96,9 @@ The module exports 34 cmdlets. Discovery cmdlets (`Get-Infisical*`) use a `List` | `Install-InfisicalCertificate` | Installs an Infisical certificate (and optional chain) into a Windows certificate store. | | `Uninstall-InfisicalCertificate` | Removes a certificate from a Windows certificate store by thumbprint, subject, or pipeline input. | | `Export-InfisicalCertificate` | Exports an Infisical certificate to disk in PEM, PFX, or CER format. | +| `Get-InfisicalScepMdmProfile` | Projects an Infisical certificate profile into a Windows SCEP MDM profile model. | +| `Export-InfisicalScepMdmProfile` | Writes a SCEP MDM profile to disk as a SyncML payload suitable for MDM delivery. | +| `Write-InfisicalScepMdmProfileToWmi`| Submits a SCEP MDM profile to the local MDM Bridge WMI provider to trigger enrollment. | Use `Get-Help -Full` for parameter details and `Get-Help about_PSInfisicalAPI` for the module overview. diff --git a/build.ps1 b/build.ps1 index 8d035ba..7be9ead 100644 --- a/build.ps1 +++ b/build.ps1 @@ -139,7 +139,10 @@ function Write-Manifest { 'ConvertTo-InfisicalCertificate', 'Install-InfisicalCertificate', 'Uninstall-InfisicalCertificate', - 'Export-InfisicalCertificate' + 'Export-InfisicalCertificate', + 'Get-InfisicalScepMdmProfile', + 'Export-InfisicalScepMdmProfile', + 'Write-InfisicalScepMdmProfileToWmi' ) AliasesToExport = @() VariablesToExport = @() @@ -204,7 +207,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') +`$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') foreach (`$expected in `$expectedCmds) { if (-not (Get-Command -Name `$expected -Module PSInfisicalAPI -ErrorAction SilentlyContinue)) { throw "Cmdlet not found: `$expected"