Redesign SSRS certificate rebind around netsh and application-id scoping #2

Merged
hannah-vernon merged 1 commit from feature/ssrs-netsh-rebind-redesign into dev 2026-08-03 22:37:12 -05:00

What does this PR do?

Redesigns Update-SsrsCertificate.ps1 to rebind SSRS to a renewed certificate
through netsh http scoped by the SSRS application id, replacing the previous
Reporting Services WMI provider (Create/RemoveSSLCertificateBinding)
approach.

Motivation, from renewing certificates on real multi-name SSRS deployments:

  • The WMI provider only manages the 0.0.0.0:443 binding. A real deployment
    also has several hostname:443 SNI bindings that live only in HTTP.SYS and
    share the SSRS application id. The provider left those stranded on the old
    certificate.
  • RemoveSSLCertificateBindings rewrites rsreportserver.config in a way that
    can strand the site, and it did so even when the call threw.

New approach:

  • The provider is used read-only to discover the instance and derive its
    application id (matched by IP:port, not by hash, so it survives a desync).
  • Every HTTP.SYS binding under that application id whose certificate differs from
    the renewed thumbprint is rebound with netsh http update sslcert (falling back
    to delete+add), covering both the IP and the hostname/SNI bindings while
    ignoring unrelated bindings.
  • rsreportserver.config is resolved from the RsConfigFilePath registry value
    first and validated before any binding is touched; its <CertificateHash>
    values are updated (with a backup) for restart-safety.
  • The operation is idempotent and restarts the service only when a binding or the
    config file actually changed.

Documentation (README.md, AGENT-README.md) updated to match.

Fixes #

How was this tested?

  • PowerShell parser validation passes ([Parser]::ParseFile() returns no errors)
  • Ran with -WhatIf - read-only discovery only, no changes made
  • Manually tested on two real SSRS 2019 instances with multiple hostname/SNI
    bindings: fresh rebind (old cert -> renewed), re-rebind, -RestartReportServer,
    and idempotent no-op re-runs, verified in a browser after an SSRS restart.

Checklist

  • I have read the Contributing Guide
  • Changes are focused - one logical change per PR
  • Script runs cleanly under Set-StrictMode -Version Latest
  • State-changing actions honor -WhatIf via SupportsShouldProcess
  • Documentation updated (if user-visible behavior changed)
  • No commented-out code or debug leftovers
  • No real certificates, keys, or infrastructure names committed
## What does this PR do? Redesigns `Update-SsrsCertificate.ps1` to rebind SSRS to a renewed certificate through `netsh http` scoped by the SSRS application id, replacing the previous Reporting Services WMI provider (`Create`/`RemoveSSLCertificateBinding`) approach. Motivation, from renewing certificates on real multi-name SSRS deployments: - The WMI provider only manages the `0.0.0.0:443` binding. A real deployment also has several `hostname:443` SNI bindings that live only in HTTP.SYS and share the SSRS application id. The provider left those stranded on the old certificate. - `RemoveSSLCertificateBindings` rewrites `rsreportserver.config` in a way that can strand the site, and it did so even when the call threw. New approach: - The provider is used read-only to discover the instance and derive its application id (matched by IP:port, not by hash, so it survives a desync). - Every HTTP.SYS binding under that application id whose certificate differs from the renewed thumbprint is rebound with `netsh http update sslcert` (falling back to `delete`+`add`), covering both the IP and the hostname/SNI bindings while ignoring unrelated bindings. - `rsreportserver.config` is resolved from the `RsConfigFilePath` registry value first and validated before any binding is touched; its `<CertificateHash>` values are updated (with a backup) for restart-safety. - The operation is idempotent and restarts the service only when a binding or the config file actually changed. Documentation (`README.md`, `AGENT-README.md`) updated to match. Fixes # ## How was this tested? - [x] PowerShell parser validation passes (`[Parser]::ParseFile()` returns no errors) - [x] Ran with `-WhatIf` - read-only discovery only, no changes made - [x] Manually tested on two real SSRS 2019 instances with multiple hostname/SNI bindings: fresh rebind (old cert -> renewed), re-rebind, `-RestartReportServer`, and idempotent no-op re-runs, verified in a browser after an SSRS restart. ## Checklist - [x] I have read the [Contributing Guide](../CONTRIBUTING.md) - [x] Changes are focused - one logical change per PR - [x] Script runs cleanly under `Set-StrictMode -Version Latest` - [x] State-changing actions honor `-WhatIf` via `SupportsShouldProcess` - [x] Documentation updated (if user-visible behavior changed) - [x] No commented-out code or debug leftovers - [x] No real certificates, keys, or infrastructure names committed
Rebind SSRS to a renewed certificate by updating every HTTP.SYS SSL
binding under the SSRS application id with netsh http, instead of the
Reporting Services WMI provider's Create/RemoveSSLCertificateBinding
methods. The provider only manages the 0.0.0.0:443 binding and leaves
hostname/SNI bindings stranded on the old certificate, and its remove
step rewrites rsreportserver.config in a way that can strand the site
even when it throws.

The provider is still used read-only to discover the instance and derive
its application id (matched by IP:port, not by hash), so the script
targets exactly the SSRS bindings and ignores unrelated ones (WinRM, dev
certificates, other applications). rsreportserver.config is resolved from
the RsConfigFilePath registry value first and validated before any
binding is touched.

The rebind is idempotent: it selects only bindings whose hash differs
from the renewed thumbprint, updates the config <CertificateHash> values
for restart-safety, and restarts the service only when something actually
changed. Validated end to end on two real SSRS 2019 instances with
multiple hostname/SNI bindings, including fresh rebind, re-rebind, restart,
and no-op re-runs.

Documentation (README.md, AGENT-README.md) updated to match.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
hannah-vernon/Install-SqlServerCertificate!2
No description provided.