function Set-SqlConnectionEncryption { [CmdletBinding()] param ( # See https://learn.microsoft.com/en-us/sql/database-engine/configure-windows/certificate-requirements?view=sql-server-ver16 [Parameter(Mandatory=$false, Position = 1, HelpMessage = "The certificate to use (or `$null to clear it), must be present in the cert:\Local Machine\My store.")] [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate, [Parameter(Mandatory=$false, HelpMessage = "The SQL Server serviceaccount that should be granted access to the certificate's private key. Optional, defaults to the MSSQLSERVER login account, usually ""NT Service\MSSQLSERVER"".")] [string]$ServiceAccount, # See https://learn.microsoft.com/en-us/sql/database-engine/configure-windows/configure-sql-server-encryption?view=sql-server-ver16#step-2-configure-encryption-settings-in-sql-server [Parameter(HelpMessage = "Wether SQL Server should accept only encrypted connections. Defaults to false.")] [switch]$ForceEncryption, # See https://learn.microsoft.com/en-us/sql/relational-databases/security/networking/tds-8 [Parameter(HelpMessage = "Wether SQL Server requires clients to specify HostNameInCertificate rather than TrustServerCertificate=true. Defaults to false.")] [switch]$ForceStrict ) $ErrorActionPreference = "Stop" # 1. Configure the Certificate, ForceEncryption and ForceStrict values that SQL Server should use # Locate the "SuperSocketNetLib" registry key that contains the encryption settings; highest first in case there are multiple versions. $regKey = (ls "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server" -Recurse | Where-Object { $_.Name -like '*SuperSocketNetLib' } | Sort-Object -Property Name -Descending) if($regKey -is [Array]) { $regKey = $regKey[0] Write-Warning "Multiple SQL instances found in the registry, using ""$($regKey.Name)""." } if($Certificate) { # The thumbprint must be in all lowercase, otherwise SQL server doesn't seem to accept it?! Set-ItemProperty $regKey.PSPath -Name "Certificate" -Value $Certificate.Thumbprint.ToLowerInvariant() } else { Set-ItemProperty $regKey.PSPath -Name "Certificate" -Value $null } # ForceStrict is only available from SQL2022 onwards Set-ItemProperty $regKey.PSPath -Name "ForceEncryption" -Value $(if($ForceEncryption) { 1 } else { 0 }) Set-ItemProperty $regKey.PSPath -Name "ForceStrict" -Value $(if($ForceStrict) { 1 } else { 0 }) # 2. Grant READ access to the $Certificate's private key if($Certificate) { # If no -ServiceAccount is specified, determine under which identity the MSSQLSERVER service runs... if(!$ServiceAccount) { $ServiceAccount = (Get-WmiObject Win32_Service -Filter "Name='MSSQLSERVER'").StartName } # The private keys are stored on the file system, determine the path for $cert # See https://stackoverflow.com/questions/40046916/how-to-grant-permission-to-user-on-certificate-private-key-using-powershell if (!$Certificate.PrivateKey) { $privateKey = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($Certificate) $privateKeyPath = "$env:ProgramData\Microsoft\Crypto\Keys\$($privateKey.Key.UniqueName)" } else { $containerName = $Certificate.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName $privateKeyPath = "$env:ProgramData\Microsoft\Crypto\RSA\MachineKeys\$containerName" } # Now grant the $ServiceAccount read permissions to the private key file $acl = Get-Acl -Path $privateKeyPath $accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule @($serviceaccount, "Read", "Allow") [void]$acl.AddAccessRule($accessRule) Set-Acl -Path $privateKeyPath -AclObject $acl } }