Repair-AcmeDnsCredential: auto-recover password, clear ARI cache, offer renewal#12
Merged
Merged
Conversation
…er renewal Three usability gaps surfaced while running the repair tool against a host whose certificate had silently failed to renew: 1. The script always prompted for the original acme-dns /register password, but operators rarely have it written down anywhere -- the password is generated once and used only programmatically by win-acme thereafter. When the existing credential file is stored with the legacy DPAPI scope, the same user that registered the domain can already decrypt it via ConvertTo-SecureString; the script now does that automatically and only prompts when auto-recovery fails. The DPAPI-LocalMachine case is also handled so re-runs are idempotent. 2. After multiple failed renewal attempts (e.g. while the DPAPI scope bug was active) Let's Encrypt rejects the next new-order with "urn:ietf:params:acme:error:alreadyReplaced" because earlier orders claimed the ARI replacement slot. The repair now finds renewal configs that reference the repaired domain and moves their cached '-temp.pfx' files into a timestamped backup subdirectory, so the next new-order request omits the ARI 'replaces' field and gets past the stale pending order. Disabled with -SkipAriCleanup. 3. Added a -RunRenewal Prompt|Yes|No parameter so the operator can trigger 'wacs.exe --renew --force --verbose' immediately after the repair. Defaults to Prompt; non-interactive sessions are treated as No so unattended RMM invocations don't hang. Version bumped to 1.1.0; comment-based help and examples updated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Improves the scripts/Recovery/Repair-AcmeDnsCredential.ps1 recovery workflow to reduce operator friction and help unblock renewals after acme-dns credential repair, including optional post-repair renewal execution.
Changes:
- Adds automatic password recovery from existing credential files for both legacy
DPAPI(CurrentUser) andDPAPI-LocalMachine. - Adds win-acme ARI cache cleanup logic by moving matching
*-temp.pfxfiles aside to avoidalreadyReplacedrenewal failures. - Adds an optional end-of-run renewal invocation (
-RunRenewal Prompt|Yes|No) and wacs.exe discovery.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Copilot review noted that the helper returned 0 in three distinct cases (win-acme dir missing, no renewal subdirs, or ran-with-nothing), so the caller couldn't tell "we skipped because of a precondition" apart from "we ran and found nothing to do". Net effect was a misleading "Nothing to clean" message stacked on top of the helper's own Write-Warning whenever a precondition wasn't met. Switch the return to a PSCustomObject with Status (Skipped|Ran) and MovedCount. The caller now only prints the summary line when the helper actually ran; the Skipped branch already explains the cause via Write-Warning, so no follow-up message is needed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three usability gaps in
scripts/Recovery/Repair-AcmeDnsCredential.ps1surfaced while recovering a host whose certificate had silently failed to renew. This PR addresses all three.1. Auto-recover the acme-dns password from the existing credential file
The repair script previously always prompted for the original acme-dns
/registerpassword, but operators almost never have it written down — the password is generated once and only used programmatically by win-acme thereafter. The existing credential file already contains the password (encrypted), and the user that originally registered the domain can decrypt it without supplying anything.Resolve-AcmeDnsPasswordhelper. When-Passwordis omitted:StorageMethod = 'DPAPI'(CurrentUser) → unwrap withConvertTo-SecureString -String(works when the script is run as the original registering user).StorageMethod = 'DPAPI-LocalMachine'→ unwrap withProtectedData::Unprotect(makes re-runs idempotent).2. Clear the win-acme ARI cache for the repaired domain
After multiple failed renewal attempts (e.g. while the DPAPI scope bug was active) Let's Encrypt rejects the next
new-orderwith:— because the earlier failed orders claimed the ARI replacement slot. The repair now finds renewal configs that reference the repaired domain and moves their cached
*-temp.pfxfiles into a timestampedari-bypass-backup-<ts>subdirectory. The nextnew-orderrequest then omits the ARIreplacesfield and gets past the stale pending order. win-acme rebuilds the cache after a successful renewal.Clear-WinAcmeAriCachehelper. Honors-WhatIf/ShouldProcess.-SkipAriCleanupswitch to disable.-WinAcmePathparameter for non-default win-acme installs (defaults to$env:ProgramData\win-acme).3. Offer to run the renewal at the end
-RunRenewal <Prompt|Yes|No>parameter, defaulting toPrompt.Noso unattended RMM invocations don't hang onRead-Host.Find-WacsExecutablesearchesC:\Tools\win-acme\and the Program Files locations.Invoke-WinAcmeRenewalrunswacs.exe --renew --force --verboseand reports the exit code.Version bumped to
1.1.0; comment-based help and examples updated.Test plan
StorageMethod = 'DPAPI', as the original registering user, with no-Password. Confirm auto-recovery succeeds and the credential file is migrated toDPAPI-LocalMachine.DPAPI-LocalMachineblob.StorageMethod = 'DPAPI'as a different admin. Confirm the script falls back to the interactive prompt with a clear message about which user to run as.-RunRenewal Yesand confirmwacs.exe --renew --force --verboseis invoked and its exit code reported.-WhatIfand confirm no destructive operations are performed (no credential file rewrite, no ARI cache file moves, no renewal invocation).-SkipAriCleanupand confirm the win-acme cache is left untouched.wacs.exe --renew --force --verbosereaches DNS validation instead of failing atnew-orderwithalreadyReplaced.Invoke-ScriptAnalyzer -Path './scripts' -Recurse -ExcludeRule PSAvoidUsingWriteHostreports no findings (already verified locally).🤖 Generated with Claude Code