Skip to content

Fix multi-service sends and certificate files, and stop Send-ScriptMessage taking pipeline input - #20

Merged
Sekers merged 9 commits into
developfrom
feature/services-and-certificate-path
Sep 24, 2026
Merged

Sekers merged 9 commits into
developfrom
feature/services-and-certificate-path

Conversation

@Sekers

@Sekers Sekers commented Sep 24, 2026

Copy link
Copy Markdown
Owner

Summary

  • Send-ScriptMessage sends through every service a request names. It put all of -Service into one MessageServiceType and looked the services up as one string, so naming two services threw "not a registered messaging service" before connecting. It now looks each service up by name, and checks every service's registration and settings before connecting to any, so a bad setting for one service stops the call before a message goes out through another. No changelog entry: MicrosoftGraph is the only service, so only a -ServiceType entry naming it twice could reach this.
  • A relative MgApp_CertificatePath now means the configuration file's folder. A file found only in PowerShell's current location is still used, with a deprecation warning, until the next major version. Send-ScriptMessage and Connect-ScriptMessage pass the configuration file's path to the service's connect handler for this.
  • Certificate files load with .NET, in memory, on both editions. CertificateFile authentication loaded the .pfx with Get-PfxCertificate, which cannot take a password on Windows PowerShell 5.1, so it required PowerShell 7.4. It now loads the file with X509Certificate2 and EphemeralKeySet in both editions. That also stops PowerShell 7 leaving a copy of the app's private key in %APPDATA%\Microsoft\Crypto\Keys after each process that connects, which every release has done. A missing certificate file is now reported by its path instead of as a missing password.
  • Connect-ScriptMessage -ServiceConfig is removed before it ships. It was added on develop so Send-ScriptMessage could hand over settings it had already read. Send-ScriptMessage now calls the connect handler directly, so nothing used the parameter, and it made a relative MgApp_CertificatePath mean something different when settings were passed in. Connect-ScriptMessage again takes exactly the parameters it had in 1.1.1.
  • Send-ScriptMessage no longer takes pipeline input. No piped value ever sent the intended message, because one value bound by value to several parameters at once, such as To, CC, BCC, Body, Attachment, and SenderId. Its body now runs in a process block, so piped input is reported as unbound and sends nothing. -ReturnConnectionInfo and -ReturnCachedContext no longer take pipeline input either, and -ReturnConnectionInfo loses Position=1, which made Connect-ScriptMessage MicrosoftGraph fail. PositionalBinding = $false keeps -Service named-only on Connect-ScriptMessage and Disconnect-ScriptMessage, as in 1.1.1. Tests/ScriptMessage.Module.Tests.ps1 enforces these rules.
  • A failed connection still stops the whole call. The services after it are not tried, and a caller that assigns the output loses the results of the ones before it. A TODO on the send loop says to change that before a second service ships, and Research_Notes/PowerShell-Language-Behavior.md section 14 measures what a caller keeps.
  • The -MailType changelog entry now says what the parameter and the MailType setting do, without "New Parameter": 1.1.1 already listed -MailType, with help that said it was not implemented.
  • Research notes: the new Research_Notes/Certificate-File-Behavior.md, and in Research_Notes/PowerShell-Language-Behavior.md the new sections 13 and 14 and a rewritten section 6.

Changelog: under Added, the two certificate file entries and the reworded -MailType entry; the Deprecated and Removed entries; a Minor: entry under Fixed; and the second entry under Security.

Live testing was sign-ins only, in a tenant that is not a test tenant: sections 5 and 6 of Research_Notes/Certificate-File-Behavior.md connected with application permissions and a throwaway certificate registered for the test. Nothing was sent.

Release classification

  • Major: breaks the public contract (an existing script or configuration file can stop working)
  • Minor: adds backward-compatible functionality
  • Patch: a backward-compatible fix
  • None: no user-visible change (tests, CI, or contributor documentation only)

Checklist

  • Targets develop, or main for a hotfix
  • CHANGELOG.md has an entry in the right section under [Unreleased] (added if missing), or the change is not user-visible
  • .\Tests\Invoke-Tests.ps1 passes
  • Contains no real tenant names, email domains, secrets, or configuration files
  • Any live Microsoft Graph testing was done in a test tenant and is described above (not a test tenant; sign-ins only, as described above)

Send-ScriptMessage put all of -Service into one MessageServiceType and
looked the services up as one string, so naming two services threw
"not a registered messaging service" before connecting. It now loops
over the services in each entry and looks each one up by name
(Research_Notes/PowerShell-Language-Behavior.md section 5).

It also checks every service's registration and settings before
connecting to any, so a bad setting for one service stops the call
before a message goes out through another. It connects through the
registered handler, as it already sent through one, rather than
through Connect-ScriptMessage.

No changelog entry: MicrosoftGraph is the only service, so only a
-ServiceType entry naming it twice could reach this.
A relative path now means the configuration file's folder. A file
found only in PowerShell's current location is still used, with a
deprecation warning, until the next major version. Settings passed to
Connect-ScriptMessage -ServiceConfig carry no file path and keep
resolving from the current location.

Send-ScriptMessage and Connect-ScriptMessage pass the file's path to
the service's connect handler as -ConfigFilePath. Which paths count as
relative is in Research_Notes/PowerShell-Language-Behavior.md
section 13. The TODO on the send loop now says what a connect failure
does to the other services.
Windows PowerShell 5.1's Get-PfxCertificate cannot take a password,
but X509Certificate2 loads the same .pfx in both editions, and
Connect-MgGraph signs in with it. Under PowerShell 7, loading with
Get-PfxCertificate or the default key storage leaves the app's
private key in the user profile after the process exits, which the
module's CertificateFile sign-in does in every release.
EphemeralKeySet writes nothing and signs in the same.

Also records that -replace with a scriptblock turns the scriptblock
into text on 5.1, and that the relative-path checks agree across
editions.
CertificateFile authentication loaded the .pfx with
Get-PfxCertificate, which cannot take a password in Windows
PowerShell 5.1, so it required PowerShell 7.4. It now loads the file
with X509Certificate2 and EphemeralKeySet in both editions, after
resolving the path with PowerShell, and expands environment variables
with [regex]::Replace, since 5.1 turns a -replace scriptblock into
text.

EphemeralKeySet also stops a leak in every release: under PowerShell
7 the default key storage left the app's private key in
%APPDATA%\Microsoft\Crypto\Keys after each process exited. A missing
certificate file is now reported by its path.

Measurements, including sign-ins on both editions with a registered
throwaway certificate, are in
Research_Notes/Certificate-File-Behavior.md.
The parameter was added on develop so Send-ScriptMessage could hand
Connect-ScriptMessage the settings it had already read.
Send-ScriptMessage now calls the service's connect handler directly,
so nothing used it, and it made a relative MgApp_CertificatePath mean
something different when settings were passed in. It was never
released, so it goes without a changelog entry. Connect-ScriptMessage
again takes exactly the parameters it had in 1.1.1 and always reads
the configuration file.

The certificate tests now hand settings to the Microsoft Graph
connect function directly.
1.1.1 already listed -MailType, with help that said it was not
implemented, and its sample configuration file had a MailType setting
that nothing read, so "New Parameter" was inaccurate. Nobody could
have relied on either, so the entry just says what the parameter and
the setting do.
A terminating error discards everything the command already wrote
to an assignment, while the pipeline, -OutVariable, and an assigned
try statement keep it. This is what makes a later service's failed
connection lose the results of the services that already sent, which
the TODO on Send-ScriptMessage's send loop covers.
No piped input ever sent the intended message: one piped value bound
by value to several parameters at once, such as To, CC, BCC, Body,
Attachment and SenderId. Send-ScriptMessage's parameters no longer
take pipeline input, and its body runs in a process block, so piped
input is reported as unbound and sends nothing instead of running the
body anyway.

The -ReturnConnectionInfo and -ReturnCachedContext switches no longer
take pipeline input, and -ReturnConnectionInfo loses Position=1, which
made "Connect-ScriptMessage MicrosoftGraph" fail and let a positional
$true turn it on. PositionalBinding = $false keeps -Service named-only
on Connect-ScriptMessage and Disconnect-ScriptMessage, as in 1.1.1.

Tests/ScriptMessage.Module.Tests.ps1 now holds these rules. The
measurements, and what by-name pipeline input would need later, are in
Research_Notes/PowerShell-Language-Behavior.md section 6.
…er skip

Invoke-Tests.ps1 now starts by listing the Pester version it will use and
why, the editions in the order they run, and the test files each edition
runs. The PSScriptAnalyzer test's skip under Windows PowerShell 5.1 now
says which edition is running and that the PowerShell 7 run does the
check, so the line makes sense read on its own in a CI log.
@Sekers
Sekers merged commit 08d540d into develop Sep 24, 2026
4 checks passed
@Sekers
Sekers deleted the feature/services-and-certificate-path branch September 24, 2026 16:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant