Skip to content

New-DbaComputerCertificate - Leave nothing behind on the computer it runs on - #10735

Open
andreasjordan wants to merge 5 commits into
developmentfrom
fix-new-dbacomputercertificate-leftovers
Open

andreasjordan wants to merge 5 commits into
developmentfrom
fix-new-dbacomputercertificate-leftovers

Conversation

@andreasjordan

@andreasjordan andreasjordan commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

Why

New-DbaComputerCertificate leaves three things behind on the computer it runs on:

  1. A keyless copy in the intermediate CA store. certreq -new with RequestType = Cert installs a self-signed certificate twice: with its key in LocalMachine\My, and without the key in LocalMachine\CA. Nothing needs that copy (a self-signed certificate is its own root, chain building gives the same result with and without it), and Remove-DbaComputerCertificate does not know about it. A lab workstation had 17 of them.
  2. The pending request of a refused submission. A request for a CA waits in LocalMachine\REQUEST with its private key. When the CA cannot be reached or refuses, the command warns and moves on, and the request with its key stays forever. The same workstation had 19 of them.
  3. The private key of a certificate made for another computer. For a remote target the certificate is created here, exported as PFX, imported there, and the local store entry is removed. The removal took the store entry only, so the private key file of the remote computer's TLS certificate stayed on the source machine.

Two smaller defects on the same paths: certreq was run without -q, so a refused submission pops a modal "Certificate Request Processor" dialog whenever the session has a window station, and the command waits for someone to click it away (seen in the lab, the same trap as in the CI runner notes). And the cleanup of the request folder passed its message to Stop-Function positionally, which throws "A positional parameter cannot be found" instead of warning.

What changes

  • After a self-signed creation the copy of the certificate is removed from LocalMachine\CA.
  • The thumbprints in LocalMachine\REQUEST are noted before certreq -new; after a refused submission every new one is removed with Remove-DbaComputerCertificate -Folder REQUEST -DeleteKey, and the warning carries the last line of the certreq output instead of an empty $_.
  • After the PFX export for a remote computer the local certificate is removed with -DeleteKey; if the key does not go, the output of that removal is reported as a warning, and the import on the target goes ahead either way.
  • All three certreq calls run with -q.
  • The Stop-Function call for the request folder gets its -Message.
  • The description of the command says that nothing stays behind.

Tests

  • Default settings context: the certificate is in LocalMachine\My and not in LocalMachine\CA.
  • Key Storage Provider context: after the certificate for the remote computer is made, neither a store entry nor a new key file is left here. Skipped with Set-ItResult when the instance runs on this computer, as on CI, where the transfer does not happen.
  • New context: a submission to nosuchca.dbatools.invalid warns with the Stop-Function message, returns nothing, and leaves LocalMachine\REQUEST and the key folders as they were. Its AfterAll removes a request the command might have left.

Verification

Lab (ADMIN01, remote target SQL03):

  • PowerShell 7.6.3: 21/21. Windows PowerShell 5.1: 21/21. Stores and key folders clean afterwards.
  • Red on old (development's command with the new test file, PowerShell 7): four failures, the CA copy exists after a local and after a remote creation, the pending request is still there, and no warning arrives because the old command shows the certreq dialog instead (closed by hand for the run). The old command cannot even clean up after that test, -DeleteKey does not exist there.

created by Claude and reviewed by Andreas Jordan

🤖 Generated with Claude Code

andreasjordan and others added 4 commits September 23, 2026 14:02
…runs on

certreq installs a self-signed certificate a second time, without its key, in
LocalMachine\CA, and that copy stayed after the certificate was removed. A
request the CA did not answer stayed in LocalMachine\REQUEST with its key. A
certificate created for another computer was removed from the local store
after the export, but its private key file stayed on the source computer.
All three are removed now, the last two through Remove-DbaComputerCertificate
-DeleteKey.

(do New-DbaComputerCertificate)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(do New-DbaComputerCertificate)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…gs of the failed request

(do New-DbaComputerCertificate)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…equest reports instead of showing a dialog

Without -q certreq shows a modal "Certificate Request Processor" dialog when
a submission is refused or the CA cannot be reached, as soon as the session
has a window station, and the command waits for someone to click it away.
The cleanup of the request folder also passed its message to Stop-Function
positionally, which threw instead of warning.

(do New-DbaComputerCertificate)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@andreasjordan
andreasjordan force-pushed the fix-new-dbacomputercertificate-leftovers branch from d81af05 to ca14220 Compare September 23, 2026 12:04
@andreasjordan
andreasjordan marked this pull request as ready for review September 23, 2026 12:41

@potatoqualitee potatoqualitee left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: restrict REQUEST-store cleanup to the request created by this invocation (public/New-DbaComputerCertificate.ps1, lines 506-517).

The snapshot at line 463 records every pre-existing request, but the failure path later treats every request created after that snapshot as its own and calls Remove-DbaComputerCertificate -Folder REQUEST -DeleteKey for each one. That selection is based on timing, not ownership.

Concrete failure: invocation A takes the snapshot and waits in certreq -submit; another enrollment process B creates an unrelated machine request; A's submission then fails. A selects both new thumbprints and removes B's request and its unique private key. The helper's shared-key safeguard cannot protect an ordinary unique request, so B can no longer accept or use its eventual certificate. I independently confirmed the exact-head selection and that -DeleteKey reaches the helper's key-deletion path when no other certificate shares the key.

Please identify this invocation's request directly (for example by matching the generated CSR's public key or a uniquely assigned key container) and delete only that request. Add real-boundary coverage proving that an unrelated request created during the submission remains in LocalMachine\REQUEST with its key intact.

…does not answer

(do New-DbaComputerCertificate)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@andreasjordan

Copy link
Copy Markdown
Collaborator Author

Fixed in 2558430. The snapshot is gone. On failure the command reads the CSR it generated itself and removes only the LocalMachine\REQUEST entry whose public key is in that CSR. Every request has its own key, so an unrelated request can never match. New integration Context "Keeps a request of someone else when the CA does not answer": only the certreq -submit call is intercepted, to create an unrelated machine request at that moment before the real submit to an unreachable CA. The test asserts that the unrelated request is the only new entry left and that its private key can still sign. On the previous head both assertions fail, because the other request was deleted. Lab: 24/24 on 5.1 and 7.

This text was created by Claude and reviewed by Andreas Jordan.

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.

2 participants