Skip to content

Get-DbaNetworkEncryption - Add command to retrieve TLS certificate from SQL Server network#10293

Draft
andreasjordan wants to merge 6 commits intodevelopmentfrom
Get-DbaNetworkEncryption
Draft

Get-DbaNetworkEncryption - Add command to retrieve TLS certificate from SQL Server network#10293
andreasjordan wants to merge 6 commits intodevelopmentfrom
Get-DbaNetworkEncryption

Conversation

@andreasjordan
Copy link
Copy Markdown
Collaborator

Adds Get-DbaNetworkEncryption which retrieves the TLS/SSL certificate presented by a SQL Server instance during the TLS handshake, without requiring Windows host access or WinRM.

Key features:

  • Connects directly to SQL Server's TCP port via TLS/SSL
  • No Windows host access required (no WinRM)
  • Handles named instances via SQL Browser (UDP 1434) with proper DNS resolution
  • Returns Subject, Issuer, Thumbprint, Expiration, DNS SANs, and more
  • Graceful error handling for instances without TLS configured

This complements Get-DbaNetworkCertificate (which reads from the Windows registry, requires WinRM) by instead showing what certificate is actually being presented to clients.

@andreasjordan
Copy link
Copy Markdown
Collaborator Author

@claude I have added two files in this branch:

  • private/functions/Get-SqlServerTlsCertificate.ps1
  • public/Get-DbaNetworkEncryption.ps1

Get-SqlServerTlsCertificate should be called from Get-DbaNetworkEncryption. We want to preserve the content of Get-SqlServerTlsCertificate as much as possible but some modifications are allowed if needed.

Please add the code that is needed to get this working.

@claude
Copy link
Copy Markdown

claude bot commented Mar 30, 2026

Claude finished @andreasjordan's task in 3m 14s —— View job


Done

  • Read both files
  • Understand the request and plan integration
  • Implement the connection between Get-DbaNetworkEncryption and Get-SqlServerTlsCertificate
  • Register command in dbatools.psd1 and dbatools.psm1
  • Commit and push changes

What was implemented

The process block of Get-DbaNetworkEncryption now:

  1. Parses DbaInstanceParameter to extract ComputerName, InstanceName, and Port
  2. Builds a splat for Get-SqlServerTlsCertificate:
    • Uses TCP connection type when a port is explicitly specified (e.g. sql01,1433)
    • Falls back to SQLBrowser (the default) for named instances without an explicit port — this queries UDP 1434 to auto-discover the TCP port
  3. Calls Get-SqlServerTlsCertificate and populates all output properties from the returned X509Certificate2
  4. Skips gracefully when no cert is returned (non-TLS instances — the private function already wrote a non-terminating error)

Port resolution logic:

  • Explicit port (e.g. ,1433) → use that port, TCP connection
  • Default instance, no port specified → report 1433
  • Named instance via SQLBrowser → report $null (actual port is resolved dynamically inside the private function)

Also registered Get-DbaNetworkEncryption in both dbatools.psd1 (FunctionsToExport) and dbatools.psm1 (Export-ModuleMember), alphabetically alongside Get-DbaNetworkCertificate.
|

github-actions bot and others added 2 commits March 30, 2026 07:54
(do Get-DbaNetworkEncryption)

Co-authored-by: Andreas Jordan <andreasjordan@users.noreply.github.com>
@andreasjordan
Copy link
Copy Markdown
Collaborator Author

The command works in my lab as expected.

@andreasjordan
Copy link
Copy Markdown
Collaborator Author

@claude we still want to support PowerShell version 3. Would it be possible to change the code of private/functions/Get-SqlServerTlsCertificate.ps1 to be compatible with PowerShell version 3? Don't change the code yet, only analyze.

@claude
Copy link
Copy Markdown

claude bot commented Mar 30, 2026

Claude finished @andreasjordan's task in 1m 31s —— View job


Analysis: PowerShell v3 Compatibility for Get-SqlServerTlsCertificate.ps1

  • Read trigger comment and understand request
  • Read private/functions/Get-SqlServerTlsCertificate.ps1
  • Identify PowerShell v3 incompatibilities
  • Post analysis

Yes, it is possible to make this compatible with PowerShell v3, but there are several changes required. Here is the full analysis:


Issue 1 — class keyword (CRITICAL, lines 114–179)

class TdsTlsStream : System.IO.Stream { ... }

PowerShell classes were introduced in v5.0. This is the biggest blocker. The entire TdsTlsStream class — which wraps a stream to prepend TDS protocol headers around each TLS payload — cannot be defined this way in PS3.

Fix approach: Compile the class at runtime using Add-Type with inline C# code. The logic of the class itself does not need to change; it just needs to live in a C# string passed to Add-Type. This is a well-established pattern in dbatools.


Issue 2 — ::new() constructor syntax (throughout the file)

[Type]::new() was introduced in PowerShell v5.0. All occurrences must be replaced with New-Object. Here are all the instances:

Line Current Fix
138 [byte[]]::new(8) New-Object byte[] 8
166 [byte[]]::new(8 + $Count) New-Object byte[] (8 + $Count)
193 [System.Net.Sockets.UdpClient]::new($ComputerName, 1434) New-Object System.Net.Sockets.UdpClient($ComputerName, 1434)
242 [System.Net.Sockets.TcpClient]::new() New-Object System.Net.Sockets.TcpClient
253–256 [System.IO.Pipes.NamedPipeClientStream]::new(...) New-Object System.IO.Pipes.NamedPipeClientStream(...)
284 [byte[]]::new(8) New-Object byte[] 8
295 [byte[]]::new($payloadLength) New-Object byte[] $payloadLength
340 [TdsTlsStream]::new($targetStream) New-Object TdsTlsStream $targetStream (after Add-Type fix)
348 [System.Net.Security.SslStream]::new(...) New-Object System.Net.Security.SslStream(...)
367 [System.Security.Cryptography.X509Certificates.X509Certificate2]::new(...) New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(...)

Issue 3 — [Ordered]@{} (lines 204, 218)

$instanceInfo = [Ordered]@{}

Ordered hashtables via [Ordered] were introduced in PowerShell v3, so this is actually fine and does not need to change.


Issue 4 — .GetAwaiter().GetResult() (line 248)

$null = $connectTask.GetAwaiter().GetResult()

GetAwaiter() is a .NET 4.5 method. PowerShell 3.0 runs on .NET 4.0, where this method does not exist. In practice most systems with PS3 also have .NET 4.5+ installed, but technically this is a risk.

Fix approach: Remove this call. Since .Wait() on line 244 already blocks until the task completes (and throws if it faulted), calling .GetAwaiter().GetResult() afterward is redundant — its only purpose here is to re-throw any exception from the task, but .Wait() already does that. The line can simply be removed.


Summary

Issue PS Version Introduced Fixable?
class keyword v5.0 Yes — Add-Type with C#
::new() constructors (×10) v5.0 Yes — New-Object
[Ordered]@{} v3.0 No change needed
.GetAwaiter().GetResult() .NET 4.5 Yes — remove the line

All issues are fixable. The most significant change is converting TdsTlsStream to a C# Add-Type block, but the internal logic of the class stays exactly the same — it's purely a syntax translation from PowerShell class syntax to C#.

@andreasjordan
Copy link
Copy Markdown
Collaborator Author

@claude please implement the suggested changes.

@claude
Copy link
Copy Markdown

claude bot commented Mar 30, 2026

Claude finished @andreasjordan's task in 3m 46s —— View job


Implementing PowerShell v3 Compatibility Changes

  • Read trigger comment and understand request
  • Read private/functions/Get-SqlServerTlsCertificate.ps1
  • Convert class TdsTlsStream to Add-Type C# block
  • Replace all ::new() calls with New-Object
  • Remove .GetAwaiter().GetResult() line
  • Commit and push changes

Done

All four PowerShell v3 incompatibilities have been fixed in private/functions/Get-SqlServerTlsCertificate.ps1:

1. class TdsTlsStreamAdd-Type C# block
The entire PowerShell class (v5+ only) was translated to an equivalent C# class compiled via Add-Type. A guard check (-not ([System.Management.Automation.PSTypeName]"TdsTlsStream").Type) prevents re-compilation if the function is called multiple times in the same session.

2. All ::new() constructors → New-Object
All 10 occurrences replaced:

  • [byte[]]::new(8)New-Object byte[] 8
  • [byte[]]::new($payloadLength)New-Object byte[] $payloadLength
  • [System.Net.Sockets.UdpClient]::new(...)New-Object -TypeName System.Net.Sockets.UdpClient -ArgumentList ...
  • [System.Net.Sockets.TcpClient]::new()New-Object -TypeName System.Net.Sockets.TcpClient
  • [System.IO.Pipes.NamedPipeClientStream]::new(...)New-Object -TypeName System.IO.Pipes.NamedPipeClientStream -ArgumentList ...
  • [TdsTlsStream]::new(...)New-Object -TypeName TdsTlsStream -ArgumentList ...
  • [System.Net.Security.SslStream]::new(...)New-Object -TypeName System.Net.Security.SslStream -ArgumentList ...
  • [System.Security.Cryptography.X509Certificates.X509Certificate2]::new(...)New-Object -TypeName ... -ArgumentList ...

3. .GetAwaiter().GetResult() removed
The .Wait() call on line 244 already blocks and re-throws any exception from the task, making .GetAwaiter().GetResult() redundant. Removed to avoid the .NET 4.5 dependency.

4. SSL validation callback extracted
The inline scriptblock delegate was extracted to a named $sslValidationCallback variable (cast to [System.Net.Security.RemoteCertificateValidationCallback]) to allow it to be passed via New-Object -ArgumentList.

github-actions bot and others added 3 commits March 30, 2026 10:04
- Replace PowerShell class with Add-Type C# block (classes require PS v5+)
- Replace all ::new() constructor calls with New-Object (PS v3 compatible)
- Remove .GetAwaiter().GetResult() call (.NET 4.0 compatible; .Wait() already handles the result)
- Extract SSL validation callback to named variable for New-Object compatibility

(do Get-DbaNetworkEncryption)

Co-authored-by: Andreas Jordan <andreasjordan@users.noreply.github.com>
@andreasjordan
Copy link
Copy Markdown
Collaborator Author

I'm very happy with this command and it works in my lab. But I would like to remove the port from the output as we don't get the used port from the private function and the output is all about the certificate and the port is not so importent.

What do you think @potatoqualitee ?

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