Repository navigation
New-DbaComputerCertificate - Leave nothing behind on the computer it runs on - #10735
Conversation
…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>
d81af05 to
ca14220
Compare
potatoqualitee
left a comment
There was a problem hiding this comment.
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>
|
Fixed in 2558430. The snapshot is gone. On failure the command reads the CSR it generated itself and removes only the This text was created by Claude and reviewed by Andreas Jordan. |
potatoqualitee
left a comment
There was a problem hiding this comment.
Two blocking ownership defects remain. (1) public/New-DbaComputerCertificate.ps1 identifies the failed request by reading $certCsr only after certreq -submit fails, but $certDir/$certCsr are derived only from the target FQDN. Concrete race: invocation A waits on submit; invocation B for the same target deletes/recreates the shared files with B's CSR; A fails, reads B's CSR, and removes B's REQUEST entry with -DeleteKey. B loses its enrollment and unique key while A's request remains. Use invocation-unique request files/directories and add overlapping same-FQDN real-boundary coverage. (2) the two new test cleanups delete every REQUEST entry created after a global snapshot. An unrelated enrollment appearing during the test is therefore deleted with its key. Track and remove only test-owned request identities/keys. I independently confirmed the shared-path construction, delayed CSR read, key-matching selection, remover deletion path, and global-delta teardown at this exact head.
…nd remove only the own request The request files lived in a folder named after the computer, shared by concurrent calls for the same computer. A second call deleted the files of the first one, and the first call read the request file only after the CA refused, so it could remove the request of another call or none. Every call now has a folder of its own, records its pending request right after certreq -new, and removes exactly that request when the CA does not issue the certificate. The folder name does not start with dbatools, because the maintenance task tempcleanup deletes such folders one minute after import, and the failure paths remove the folder too. The tests know their requests by the keys in the request files certreq wrote for them, and remove only those. A new test runs a second call for the same computer while the first one waits for the CA. (do New-DbaComputerCertificate) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks, both confirmed and fixed in fd9c24b. (1) Request ownership in the command
The first version of the new folder name started with (2) Test cleanup
Overlap coverage Lab: New-DbaComputerCertificate.Tests.ps1 27/27, twice each on Windows PowerShell 5.1 and PowerShell 7.6.3. REQUEST, My and CA stores, key folders and temp folder are back to their state before the runs. This text was created by Claude and reviewed by Andreas Jordan. |
potatoqualitee
left a comment
There was a problem hiding this comment.
Blocking certificate-loss path in public/New-DbaComputerCertificate.ps1:500-532: every nonempty certreq -submit response that does not contain ssued enters the failure branch and deletes this invocation's REQUEST entry with -DeleteKey. A successful submission awaiting CA manager approval returns a pending response, so it reaches this branch too.
Concrete reproduction: use a template that requires CA manager approval, run New-DbaComputerCertificate, then approve the returned request ID and retrieve the issued certificate. The command has already removed the pending request and its unique private key, so the issued certificate cannot become a usable TLS certificate. Microsoft documents that CA policy may leave a submission pending and return a request ID for later certreq -retrieve: https://learn.microsoft.com/windows-server/administration/windows-commands/certreq_1#certreq--retrieve
Please distinguish accepted/pending submissions from refusal or terminal failure, preserve the pending REQUEST/key and surface its request ID for later completion, and add real-CA coverage for pending → approval → retrieve/accept. The earlier request-ownership and same-FQDN races are fixed at this head; this remaining path is specifically about valid pending enrollment.
…est ID A CA can hold a request as pending, for example until a CA manager approves it. certreq ends with 0 then and writes no certificate file. The command took every submission without "ssued" in its output for a failure and removed the request with its private key, so the certificate the CA issued later could never be installed. The outcome now comes from the exit code of certreq and the certificate file, not from the localized messages. That also fixes a refused request: "Certificate not issued" contains "ssued", so the command tried to install a certificate that was never written and left the request behind. A pending request stays in LocalMachine\REQUEST with its key, and the warning names the request ID and the certreq commands that install the certificate once it is issued. New lab-only test contexts cover pending, approval, retrieve and accept, and a refusal, against a real CA. (do New-DbaComputerCertificate) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks, confirmed and fixed in 9f11387. The lab CA shows three outcomes of
So the command now decides by the exit code and the certificate file, not by the localized messages:
That fixes a second defect on this path. Real-CA coverage runs against our lab CA. I added a template there that holds every request until a CA manager approves it (
No CI environment has a CA, so both skip there. They read three new Lab: New-DbaComputerCertificate.Tests.ps1 32/32 on Windows PowerShell 5.1 and PowerShell 7.6.3, nothing skipped. Against the previous head, all five new tests fail. The REQUEST and My stores, the key folders and the temp folder are back to their state before the runs. This text was created by Claude and reviewed by Andreas Jordan. |
potatoqualitee
left a comment
There was a problem hiding this comment.
Reviewed the exact current head. The prior request-ownership, same-target concurrency, test-cleanup, and pending-CA certificate-loss blockers are addressed. The request is now identified from its own CSR/key, per-call temp paths remove the shared-path race, pending CA submissions preserve the REQUEST entry/private key and provide retrieval instructions, refusal cleanup remains scoped to the invocation, and the current CI run is green. I found no remaining material defect.
|
thank you 👌🏼 |
Why
New-DbaComputerCertificateleaves three things behind on the computer it runs on:certreq -newwithRequestType = Certinstalls a self-signed certificate twice: with its key inLocalMachine\My, and without the key inLocalMachine\CA. Nothing needs that copy (a self-signed certificate is its own root, chain building gives the same result with and without it), andRemove-DbaComputerCertificatedoes not know about it. A lab workstation had 17 of them.LocalMachine\REQUESTwith 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.Two smaller defects on the same paths:
certreqwas 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 toStop-Functionpositionally, which throws "A positional parameter cannot be found" instead of warning.What changes
LocalMachine\CA.LocalMachine\REQUESTare noted beforecertreq -new; after a refused submission every new one is removed withRemove-DbaComputerCertificate -Folder REQUEST -DeleteKey, and the warning carries the last line of the certreq output instead of an empty$_.-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.certreqcalls run with-q.Stop-Functioncall for the request folder gets its-Message.Tests
LocalMachine\Myand not inLocalMachine\CA.Set-ItResultwhen the instance runs on this computer, as on CI, where the transfer does not happen.nosuchca.dbatools.invalidwarns with the Stop-Function message, returns nothing, and leavesLocalMachine\REQUESTand the key folders as they were. Its AfterAll removes a request the command might have left.Verification
Lab (ADMIN01, remote target SQL03):
-DeleteKeydoes not exist there.created by Claude and reviewed by Andreas Jordan
🤖 Generated with Claude Code