diff --git a/CHANGELOG.md b/CHANGELOG.md index 7469403..4374a65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +2.6.4 +* Fixed an issue with SSL Flags greater than 3 were not being applied correctly to newer IIS servers. +* Fixed an issue when formatting private RSA keys when connecting using the ssh protocol. +* When using ssh protocol in containers, the SQL ACL on private keys was not being updating correctly. This has been fixed. + 2.6.3 * Fixed re-enrollment or ODKG job when RDN Components contained escaped commas. * Updated renewal job for IIS Certs to delete the old cert if not bound or used by other web sites. diff --git a/IISU/PSHelper.cs b/IISU/PSHelper.cs index acd9831..5b10c2d 100644 --- a/IISU/PSHelper.cs +++ b/IISU/PSHelper.cs @@ -635,9 +635,10 @@ private string createPrivateKeyFile() private static string formatPrivateKey(string privateKey) { - String keyType = privateKey.Contains("OPENSSH PRIVATE KEY") ? "OPENSSH" : "RSA"; + string header = privateKey.Substring(0, privateKey.IndexOf("KEY-----") + 8); + string footer = privateKey.Substring(privateKey.IndexOf("-----END")); - return privateKey.Replace($" {keyType} PRIVATE ", "^^^").Replace(" ", System.Environment.NewLine).Replace("^^^", $" {keyType} PRIVATE ") + System.Environment.NewLine; + return privateKey.Replace(header, "HEADER").Replace(footer, "FOOTER").Replace(" ", Environment.NewLine).Replace("HEADER", header).Replace("FOOTER", footer) + Environment.NewLine; } } } diff --git a/IISU/PowerShellScripts/WinCertScripts.ps1 b/IISU/PowerShellScripts/WinCertScripts.ps1 index 00f7fdf..03a5115 100644 --- a/IISU/PowerShellScripts/WinCertScripts.ps1 +++ b/IISU/PowerShellScripts/WinCertScripts.ps1 @@ -1,16 +1,21 @@ -# Version 1.3.0 +# Version 1.4.0 # Summary # Contains PowerShell functions to execute administration jobs for general Windows certificates, IIS and SQL Server. # There are additional supporting PowerShell functions to support job specific actions. # Update notes: -# 08/12/25 Updated functions to manage IIS bindings and certificates -# Updated script to read CSPs correctly using newer CNG Keys -# Fix an error with complex PFX passwords having irregular characters -# 08/29/25 Fixed the add cert to store function to return the correct thumbprint -# Made changes to the IIS Binding logic, breaking it into manageable pieces to aid in debugging issues -# 09/16/25 Updated the Get CSP function to handle null values when reading hybrid certificates +# Date Version Description +# 08/12/25 2.6.x Updated functions to manage IIS bindings and certificates +# Updated script to read CSPs correctly using newer CNG Keys +# Fix an error with complex PFX passwords having irregular characters +# 08/29/25 2.6.x Fixed the add cert to store function to return the correct thumbprint +# Made changes to the IIS Binding logic, breaking it into manageable pieces to aid in debugging issues +# 09/16/25 2.6.3 Updated the Get CSP function to handle null values when reading hybrid certificates +# 11/17/25 2.6.4 Fixed issue with SSL Flags not being applied correctly to IIS bindings +# 11/21/25 Renamed Set-KFCertificateBinding to Set-KFSQLCertificateBinding +# Fixed the Set-KFSQLCertificateBinding function to correctly bind and set the ACL permissions on the private key when using Windows-to-Windows and SSH-based remote connections. +# Updated the Set-KFSQLCertificateBinding to handle both CNG (modern) and CAPI (legacy) certificate key storage providers when setting ACLs on private keys. # Set preferences globally at the script level $DebugPreference = "Continue" @@ -388,17 +393,12 @@ function New-KFIISSiteBinding { param ( [Parameter(Mandatory = $true)] [string]$SiteName, - [string]$IPAddress = "*", - [int]$Port = 443, - [AllowEmptyString()] [string]$Hostname = "", - [ValidateSet("http", "https")] [string]$Protocol = "https", - [ValidateScript({ if ($Protocol -eq 'https' -and [string]::IsNullOrEmpty($_)) { throw "Thumbprint is required when Protocol is 'https'" @@ -406,56 +406,97 @@ function New-KFIISSiteBinding { $true })] [string]$Thumbprint, - [string]$StoreName = "My", - [int]$SslFlags = 0 ) Write-Information "Entering PowerShell Script: New-KFIISSiteBinding" -InformationAction SilentlyContinue - Write-Verbose "Function: New-KFIISSiteBinding" Write-Verbose "Parameters: $(($PSBoundParameters.GetEnumerator() | ForEach-Object { "$($_.Key): '$($_.Value)'" }) -join ', ')" try { - # This function mimics IIS Manager behavior: - # - Replaces exact binding matches (same IP:Port:Hostname) - # - Allows multiple bindings with different hostnames (SNI) - # - Lets IIS handle true conflicts rather than pre-checking - - # Step 1: Verify site exists and get management approach + # Step 1: Perform verifications and get management info + # Check SslFlags + if (-not (Test-ValidSslFlags -SslFlags $SslFlags)) { + return New-ResultObject -Status Error 400 -Step "Validation" -ErrorMessage "Invalid SSL Flag bit configuration ($SslFlags)" + } + $managementInfo = Get-IISManagementInfo -SiteName $SiteName if (-not $managementInfo.Success) { return $managementInfo.Result } - # Step 2: Remove existing HTTPS bindings for this exact binding information - # This mimics IIS behavior: replace exact matches, allow different hostnames + # Step 2: Remove existing HTTPS bindings for this binding info $searchBindings = "${IPAddress}:${Port}:${Hostname}" Write-Verbose "Removing existing HTTPS bindings for: $searchBindings" - + $removalResult = Remove-ExistingIISBinding -SiteName $SiteName -BindingInfo $searchBindings -UseIISDrive $managementInfo.UseIISDrive if ($removalResult.Status -eq 'Error') { return $removalResult } - # Step 3: Add new binding with SSL certificate - Write-Verbose "Adding new binding with SSL certificate" - + # Step 3: Determine SslFlags supported by Microsoft.Web.Administration + if ($SslFlags -gt 3) { + Write-Verbose "SslFlags value $SslFlags exceeds managed API range (0–3). Applying reduced flags for creation." + $SslFlagsApplied = ($SslFlags -band 3) + } else { + $SslFlagsApplied = $SslFlags + } + + # Step 4: Add the new binding with the reduced flag set + Write-Verbose "Adding new binding with SSL certificate (SslFlagsApplied=$SslFlagsApplied)" + $addParams = @{ - SiteName = $SiteName - Protocol = $Protocol - IPAddress = $IPAddress - Port = $Port - Hostname = $Hostname - Thumbprint = $Thumbprint - StoreName = $StoreName - SslFlags = $SslFlags + SiteName = $SiteName + Protocol = $Protocol + IPAddress = $IPAddress + Port = $Port + Hostname = $Hostname + Thumbprint = $Thumbprint + StoreName = $StoreName + SslFlags = $SslFlagsApplied UseIISDrive = $managementInfo.UseIISDrive } - + $addResult = Add-IISBindingWithSSL @addParams - return $addResult + if ($addResult.Status -eq 'Error') { + return $addResult + } + + # Step 5: If extended flags, update via appcmd.exe + if ($SslFlags -gt 3) { + Write-Verbose "Applying full SslFlags=$SslFlags via appcmd" + + $appcmd = Join-Path $env:windir "System32\inetsrv\appcmd.exe" + + # Escape any single quotes in hostname + $safeHostname = $Hostname -replace "'", "''" + $bindingInfo = "${IPAddress}:${Port}:${safeHostname}" + + # Quote site name only if it contains spaces + if ($SiteName -match '\s') { + $siteArg = "/site.name:`"$SiteName`"" + } else { + $siteArg = "/site.name:$SiteName" + } + + # Build binding argument for appcmd + $bindingArg = "/bindings.[protocol='https',bindingInformation='$bindingInfo'].sslFlags:$SslFlags" + + Write-Verbose "Running appcmd: $appcmd $siteArg $bindingArg" + $appcmdOutput = & $appcmd set site $siteArg $bindingArg 2>&1 + Write-Verbose "appcmd output: $appcmdOutput" + + #& $appcmd set site $siteArg $bindingArg | Out-Null + + if ($LASTEXITCODE -ne 0) { + Write-Warning "appcmd failed to set extended SslFlags ($SslFlags) for binding $bindingInfo." + } else { + Write-Verbose "Successfully updated SslFlags to $SslFlags via appcmd." + } + } + + return $addResult } catch { $errorMessage = "Unexpected error in New-KFIISSiteBinding: $($_.Exception.Message)" @@ -851,10 +892,10 @@ function Bind-KFSqlCertificate { if ($RenewalThumbprint -and $RenewalThumbprint -contains $currentThumbprint) { Write-Information "Renewal thumbprint matches for instance: $fullInstance" - $result = Set-KFCertificateBinding -InstanceName $instance -NewThumbprint $NewThumbprint -RestartService:$RestartService + $result = Set-KFSQLCertificateBinding -InstanceName $instance -NewThumbprint $NewThumbprint -RestartService:$RestartService } elseif (-not $RenewalThumbprint) { Write-Information "No renewal thumbprint provided. Binding certificate to instance: $fullInstance" - $result = Set-KFCertificateBinding -InstanceName $instance -NewThumbprint $NewThumbprint -RestartService:$RestartService + $result = Set-KFSQLCertificateBinding -InstanceName $instance -NewThumbprint $NewThumbprint -RestartService:$RestartService } if (-not $result) { @@ -876,93 +917,552 @@ function Bind-KFSqlCertificate { return $bindingSuccess } -function Set-KFCertificateBinding { +function Set-KFSQLCertificateBinding { + <# + .SYNOPSIS + Binds a certificate to a SQL Server instance and sets appropriate permissions. + + .DESCRIPTION + This function binds a certificate to a SQL Server instance by updating the registry, + setting ACL permissions on the private key, and optionally restarting the SQL service. + Supports both local Windows-to-Windows and SSH-based remote connections. + Handles both CNG (modern) and CAPI (legacy) certificate key storage. + + .PARAMETER InstanceName + The SQL Server instance name (e.g., "MSSQLSERVER" for default instance) + + .PARAMETER NewThumbprint + The thumbprint of the certificate to bind + + .PARAMETER RestartService + Switch to restart the SQL Server service after binding + + .EXAMPLE + Set-KFCertificateBinding -InstanceName "MSSQLSERVER" -NewThumbprint "ABC123..." -RestartService + #> + + [CmdletBinding()] param ( + [Parameter(Mandatory = $true)] [string]$InstanceName, + + [Parameter(Mandatory = $true)] [string]$NewThumbprint, + [switch]$RestartService ) - - Write-Information "Binding certificate with thumbprint $NewThumbprint to instance $InstanceName..." - + + Write-Information "Starting certificate binding process for instance: $InstanceName" + Write-Information "Target certificate thumbprint: $NewThumbprint" + try { - # Get the full SQL instance name from the registry - $fullInstance = Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL" -Name $InstanceName -ErrorAction Stop - $RegistryPath = Get-SqlCertRegistryLocation -InstanceName $fullInstance - - Write-Verbose "Full instance: $fullInstance" - Write-Verbose "Registry Path: $RegistryPath" - - # Attempt to update the registry + # ============================================================ + # STEP 1: Get SQL Instance Registry Path + # ============================================================ + Write-Information "Retrieving SQL Server instance information..." + + try { + $fullInstance = Get-ItemPropertyValue -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL" -Name $InstanceName -ErrorAction Stop + $RegistryPath = Get-SqlCertRegistryLocation -InstanceName $fullInstance + + Write-Verbose "Full instance name: $fullInstance" + Write-Verbose "Registry path: $RegistryPath" + Write-Information "SQL Server instance registry path located: $RegistryPath" + } + catch { + Write-Error "Failed to locate SQL Server instance '$InstanceName' in registry: $_" + throw $_ + } + + # ============================================================ + # STEP 2: Update Registry with New Certificate Thumbprint + # ============================================================ + Write-Information "Updating registry with new certificate thumbprint..." + try { + # Backup current value + $currentThumbprint = Get-ItemPropertyValue -Path $RegistryPath -Name "Certificate" -ErrorAction SilentlyContinue + + if ($currentThumbprint) { + Write-Verbose "Current certificate thumbprint: $currentThumbprint" + } else { + Write-Verbose "No existing certificate thumbprint found" + } + + # Set new thumbprint Set-ItemProperty -Path $RegistryPath -Name "Certificate" -Value $NewThumbprint -ErrorAction Stop - Write-Information "Updated registry for instance $InstanceName with new thumbprint." + + # Verify the change + $verifyThumbprint = Get-ItemPropertyValue -Path $RegistryPath -Name "Certificate" -ErrorAction Stop + + if ($verifyThumbprint -eq $NewThumbprint) { + Write-Information "Registry updated successfully" + } else { + throw "Registry update verification failed. Expected: $NewThumbprint, Got: $verifyThumbprint" + } } catch { - Write-Error "Failed to update registry at {$RegistryPath}: $_" - throw $_ # Rethrow the error to ensure it's caught at a higher level + Write-Error "Failed to update registry at '$RegistryPath': $_" + throw $_ } - - # Retrieve SQL Server service user - $serviceName = Get-SqlServiceName -InstanceName $InstanceName - $serviceInfo = Get-CimInstance -ClassName Win32_Service -Filter "Name='$serviceName'" -ErrorAction Stop - $SqlServiceUser = $serviceInfo.StartName - - if (-not $SqlServiceUser) { - throw "Unable to retrieve service account for SQL Server instance: $InstanceName" + + # ============================================================ + # STEP 3: Get SQL Server Service Information + # ============================================================ + Write-Information "Retrieving SQL Server service information..." + + try { + $serviceName = Get-SqlServiceName -InstanceName $InstanceName + $serviceInfo = Get-CimInstance -ClassName Win32_Service -Filter "Name='$serviceName'" -ErrorAction Stop + $SqlServiceUser = $serviceInfo.StartName + + if (-not $SqlServiceUser) { + throw "Unable to retrieve service account for SQL Server instance: $InstanceName" + } + + # Normalize service account name for ACL operations + if ($SqlServiceUser -eq "LocalSystem") { + $SqlServiceUser = "NT AUTHORITY\SYSTEM" + Write-Verbose "Normalized LocalSystem to: $SqlServiceUser" + } + elseif ($SqlServiceUser -match "^NT Service\\") { + # NT Service accounts are already in correct format + Write-Verbose "Using NT Service account: $SqlServiceUser" + } + elseif ($SqlServiceUser.StartsWith(".\")) { + # Local account - convert to machine\user format + $SqlServiceUser = "$env:COMPUTERNAME$($SqlServiceUser.Substring(1))" + Write-Verbose "Normalized local account to: $SqlServiceUser" + } + + Write-Verbose "Service name: $serviceName" + Write-Information "SQL Server service account: $SqlServiceUser" } - - Write-Verbose "Service Name: $serviceName" - Write-Verbose "SQL Service User: $SqlServiceUser" - Write-Information "SQL Server service account for ${InstanceName}: $SqlServiceUser" - - # Retrieve the certificate - $Cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Thumbprint -eq $NewThumbprint } - if (-not $Cert) { - throw "Certificate with thumbprint $NewThumbprint not found in LocalMachine\My store." + catch { + Write-Error "Failed to retrieve SQL Server service information: $_" + throw $_ } - - # Retrieve private key path - $privKey = $Cert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName - $keyPath = "$($env:ProgramData)\Microsoft\Crypto\RSA\MachineKeys\" - $privKeyPath = Get-Item "$keyPath\$privKey" -ErrorAction Stop - Write-Information "Private Key Path is: $privKeyPath" + + # ============================================================ + # STEP 4: Locate Certificate and Private Key + # ============================================================ + Write-Information "Locating certificate and private key..." try { - # Set ACL for the certificate private key - $Acl = Get-Acl -Path $privKeyPath -ErrorAction Stop - $Ar = New-Object System.Security.AccessControl.FileSystemAccessRule($SqlServiceUser, "Read", "Allow") - $Acl.SetAccessRule($Ar) + # Get the certificate from the LocalMachine\My store + $Cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Thumbprint -eq $NewThumbprint } + + if (-not $Cert) { + throw "Certificate with thumbprint $NewThumbprint not found in LocalMachine\My store" + } + + Write-Verbose "Certificate found: $($Cert.Subject)" + + if (-not $Cert.HasPrivateKey) { + throw "Certificate does not have a private key" + } + + # Detect private key location (CNG vs CAPI) + $privKeyPath = $null + $privKey = $null + $keyStorageType = $null + + # Try CNG first (modern certificates) + try { + $rsaKey = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($Cert) - Set-Acl -Path $privKeyPath.FullName -AclObject $Acl -ErrorAction Stop - Write-Information "Updated ACL for private key at $privKeyPath." + if ($rsaKey -is [System.Security.Cryptography.RSACng]) { + $privKey = $rsaKey.Key.UniqueName + $keyStorageType = "CNG" + Write-Verbose "Certificate uses CNG key storage" + Write-Verbose "CNG key unique name: $privKey" + + # CNG keys can be in multiple locations - check them all + $possiblePaths = @( + "$($env:ProgramData)\Microsoft\Crypto\Keys\$privKey", + "$($env:ProgramData)\Microsoft\Crypto\SystemKeys\$privKey", + "$($env:ProgramData)\Microsoft\Crypto\RSA\MachineKeys\$privKey" + ) + + Write-Verbose "Searching for CNG private key in known locations..." + foreach ($path in $possiblePaths) { + Write-Verbose "Checking: $path" + if (Test-Path $path) { + $privKeyPath = Get-Item $path -ErrorAction Stop + Write-Verbose "Found CNG private key at: $path" + break + } + } + + # If not found in standard locations, search more broadly + if (-not $privKeyPath) { + Write-Verbose "Key not found in standard locations. Searching all Crypto directories..." + + $searchPaths = @( + "$($env:ProgramData)\Microsoft\Crypto\Keys", + "$($env:ProgramData)\Microsoft\Crypto\SystemKeys", + "$($env:ProgramData)\Microsoft\Crypto\RSA\MachineKeys" + ) + + foreach ($searchPath in $searchPaths) { + if (Test-Path $searchPath) { + $found = Get-ChildItem -Path $searchPath -Filter "*$privKey*" -ErrorAction SilentlyContinue | Select-Object -First 1 + if ($found) { + $privKeyPath = $found + Write-Verbose "Found CNG private key at: $($privKeyPath.FullName)" + break + } + } + } + } + + if ($privKeyPath) { + Write-Information "Certificate uses CNG (Cryptography Next Generation) key storage" + } + } + } + catch { + Write-Verbose "CNG key detection failed or not applicable: $_" + Write-Verbose "Exception type: $($_.Exception.GetType().FullName)" + } + + # Fallback to CAPI/CSP (legacy certificates) + if (-not $privKey -or -not $privKeyPath) { + Write-Verbose "Attempting CAPI/CSP key detection..." + + try { + if ($Cert.PrivateKey -and $Cert.PrivateKey.CspKeyContainerInfo) { + $privKey = $Cert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName + $keyPath = "$($env:ProgramData)\Microsoft\Crypto\RSA\MachineKeys\" + $keyStorageType = "CAPI/CSP" + + Write-Verbose "CAPI/CSP key unique name: $privKey" + Write-Verbose "Expected path: $keyPath$privKey" + + $privKeyPath = Get-Item "$keyPath\$privKey" -ErrorAction Stop + Write-Information "Certificate uses CAPI/CSP (legacy) key storage" + } + } + catch { + Write-Verbose "CAPI/CSP key detection failed: $_" + } + } + + if (-not $privKey) { + throw "Unable to locate private key for certificate. The certificate may not have an accessible private key." + } + + if (-not $privKeyPath) { + # Last resort: try to find the key file by searching + Write-Warning "Private key file not found in expected locations. Attempting comprehensive search..." + + $allCryptoPaths = @( + "$($env:ProgramData)\Microsoft\Crypto\Keys", + "$($env:ProgramData)\Microsoft\Crypto\SystemKeys", + "$($env:ProgramData)\Microsoft\Crypto\RSA\MachineKeys" + ) + + foreach ($searchPath in $allCryptoPaths) { + if (Test-Path $searchPath) { + Write-Verbose "Searching in: $searchPath" + $found = Get-ChildItem -Path $searchPath -File -ErrorAction SilentlyContinue | + Where-Object { $_.Name -like "*$privKey*" -or $_.Name -eq $privKey } | + Select-Object -First 1 + + if ($found) { + $privKeyPath = $found + Write-Information "Private key found at: $($privKeyPath.FullName)" + break + } + } + } + + if (-not $privKeyPath) { + throw "Unable to locate private key file for certificate with thumbprint $NewThumbprint. Key name: $privKey" + } + } + + Write-Information "Private key located at: $($privKeyPath.FullName)" + Write-Verbose "Key storage type: $keyStorageType" + Write-Verbose "Key file size: $($privKeyPath.Length) bytes" + + # Verify we can read the key file + try { + $acl = Get-Acl -Path $privKeyPath.FullName -ErrorAction Stop + Write-Verbose "Successfully accessed private key file ACL" + } + catch { + Write-Warning "Could not read ACL from private key file: $_" + } + } + catch { + Write-Error "Failed to locate certificate or private key: $_" + throw $_ + } + + # ============================================================ + # STEP 5: Set ACL Permissions on Private Key + # ============================================================ + Write-Information "Setting ACL permissions on private key for SQL service account..." + + try { + $aclSet = $false + $aclMethod = $null + + # Attempt 1: Try Set-Acl (works in most local scenarios and some SSH sessions) + try { + Write-Verbose "Attempting ACL update using Set-Acl method..." + + $Acl = Get-Acl -Path $privKeyPath -ErrorAction Stop + $Ar = New-Object System.Security.AccessControl.FileSystemAccessRule( + $SqlServiceUser, + "Read", + "Allow" + ) + $Acl.SetAccessRule($Ar) + Set-Acl -Path $privKeyPath.FullName -AclObject $Acl -ErrorAction Stop + + # Verify the ACL was actually set + $verifyAcl = Get-Acl -Path $privKeyPath + $hasPermission = $verifyAcl.Access | Where-Object { + ($_.IdentityReference.Value -eq $SqlServiceUser -or + $_.IdentityReference.Value -like "*$SqlServiceUser*") -and + $_.FileSystemRights -match "Read" + } + + if ($hasPermission) { + Write-Information "ACL updated successfully using Set-Acl method" + $aclSet = $true + $aclMethod = "Set-Acl" + } else { + Write-Warning "Set-Acl completed but verification failed. Permissions may not be set correctly." + } + } + catch { + Write-Warning "Set-Acl method failed: $_" + Write-Verbose "Error details: $($_.Exception.Message)" + } + + # Attempt 2: Use icacls (more reliable in SSH sessions) + if (-not $aclSet) { + Write-Verbose "Attempting ACL update using icacls method..." + + try { + # Execute icacls to grant Read permissions + $icaclsResult = & icacls.exe $privKeyPath.FullName /grant "${SqlServiceUser}:(R)" 2>&1 + + if ($LASTEXITCODE -eq 0) { + Write-Verbose "icacls command executed successfully" + + # Verify with icacls + $verifyResult = & icacls.exe $privKeyPath.FullName 2>&1 + + if ($verifyResult -match [regex]::Escape($SqlServiceUser)) { + Write-Information "ACL updated successfully using icacls method" + $aclSet = $true + $aclMethod = "icacls" + } else { + Write-Warning "icacls completed but verification failed" + } + } else { + Write-Warning "icacls failed with exit code $LASTEXITCODE" + Write-Verbose "icacls output: $icaclsResult" + } + } + catch { + Write-Warning "icacls method failed: $_" + } + } + + # Attempt 3: Use Scheduled Task (fallback for restricted SSH sessions) + if (-not $aclSet) { + Write-Warning "Standard ACL methods failed. Attempting scheduled task method (elevated privileges)..." + + try { + # Create a temporary script to set the ACL + $tempScriptPath = Join-Path $env:TEMP "SetCertACL_$((Get-Date).Ticks).ps1" + + $scriptContent = @" +try { + `$privKeyPath = '$($privKeyPath.FullName)' + `$SqlServiceUser = '$SqlServiceUser' + + # Try icacls first + `$result = & icacls.exe `$privKeyPath /grant "`${SqlServiceUser}:(R)" 2>&1 + + if (`$LASTEXITCODE -eq 0) { + Set-Content -Path '$env:TEMP\acl_success.txt' -Value "Success via icacls" + } else { + # Fallback to Set-Acl + `$Acl = Get-Acl -Path `$privKeyPath + `$Ar = New-Object System.Security.AccessControl.FileSystemAccessRule( + `$SqlServiceUser, + 'Read', + 'Allow' + ) + `$Acl.SetAccessRule(`$Ar) + Set-Acl -Path `$privKeyPath -AclObject `$Acl + Set-Content -Path '$env:TEMP\acl_success.txt' -Value "Success via Set-Acl" + } +} catch { + Set-Content -Path '$env:TEMP\acl_error.txt' -Value `$_.Exception.Message +} +"@ + + Set-Content -Path $tempScriptPath -Value $scriptContent + Write-Verbose "Created temporary script: $tempScriptPath" + + # Create and register the scheduled task + $taskName = "SetCertACL_$((Get-Date).Ticks)" + $action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-ExecutionPolicy Bypass -NoProfile -File `"$tempScriptPath`"" + $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddSeconds(2) + $principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest + $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable + + Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings -ErrorAction Stop | Out-Null + Write-Verbose "Scheduled task registered: $taskName" + + # Wait for task to complete + Write-Verbose "Waiting for scheduled task to complete..." + Start-Sleep -Seconds 5 + + # Check results + if (Test-Path "$env:TEMP\acl_success.txt") { + $successMessage = Get-Content "$env:TEMP\acl_success.txt" -Raw + Write-Information "ACL updated successfully using scheduled task method ($successMessage)" + Remove-Item "$env:TEMP\acl_success.txt" -Force -ErrorAction SilentlyContinue + $aclSet = $true + $aclMethod = "Scheduled Task" + } + elseif (Test-Path "$env:TEMP\acl_error.txt") { + $errorMessage = Get-Content "$env:TEMP\acl_error.txt" -Raw + Remove-Item "$env:TEMP\acl_error.txt" -Force -ErrorAction SilentlyContinue + throw "Scheduled task failed: $errorMessage" + } + else { + throw "Scheduled task did not complete or produce expected output" + } + + # Cleanup + Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue + Remove-Item $tempScriptPath -Force -ErrorAction SilentlyContinue + } + catch { + Write-Warning "Scheduled task method failed: $_" + } + } + + # Final check + if (-not $aclSet) { + throw "Failed to set ACL permissions using all available methods (Set-Acl, icacls, Scheduled Task)" + } + + Write-Information "ACL permissions configured successfully using: $aclMethod" + } catch { Write-Error "Failed to update ACL on the private key: $_" + Write-Error "SQL Server may not be able to use this certificate without proper permissions." throw $_ } - - # Optionally restart the SQL Server service + + # ============================================================ + # STEP 6: Restart SQL Server Service (Optional) + # ============================================================ if ($RestartService) { + Write-Information "Restarting SQL Server service..." + try { - Write-Information "Restarting SQL Server service: $serviceName..." - Restart-Service -Name $serviceName -Force -ErrorAction Stop - Write-Information "SQL Server service restarted successfully." + # Get current service status + $service = Get-Service -Name $serviceName -ErrorAction Stop + $originalStatus = $service.Status + + Write-Verbose "Current service status: $originalStatus" + + # Stop the service if running + if ($originalStatus -eq 'Running') { + Write-Information "Stopping SQL Server service: $serviceName" + Stop-Service -Name $serviceName -Force -ErrorAction Stop + + # Wait for service to stop (with timeout) + $stopTimeout = 60 + $elapsed = 0 + + while ((Get-Service -Name $serviceName).Status -ne 'Stopped' -and $elapsed -lt $stopTimeout) { + Start-Sleep -Seconds 2 + $elapsed += 2 + Write-Verbose "Waiting for service to stop... ($elapsed seconds)" + } + + if ((Get-Service -Name $serviceName).Status -ne 'Stopped') { + throw "Service did not stop within $stopTimeout seconds" + } + + Write-Information "SQL Server service stopped successfully" + } + + # Start the service + Write-Information "Starting SQL Server service: $serviceName" + Start-Service -Name $serviceName -ErrorAction Stop + + # Wait for service to start (with timeout) + $startTimeout = 90 + $elapsed = 0 + + while ((Get-Service -Name $serviceName).Status -ne 'Running' -and $elapsed -lt $startTimeout) { + Start-Sleep -Seconds 2 + $elapsed += 2 + Write-Verbose "Waiting for service to start... ($elapsed seconds)" + } + + $finalStatus = (Get-Service -Name $serviceName).Status + + if ($finalStatus -eq 'Running') { + Write-Information "SQL Server service restarted successfully" + } else { + throw "Service did not start within $startTimeout seconds. Current status: $finalStatus" + } } catch { Write-Error "Failed to restart SQL Server service: $_" - throw $_ + Write-Warning "Certificate binding completed but service restart failed." + Write-Warning "Please restart SQL Server manually to apply the certificate binding." + Write-Warning "You can restart using: Restart-Service -Name '$serviceName' -Force" + + # Don't throw here - the certificate binding succeeded + # Just warn the user to restart manually } + } else { + Write-Information "Service restart skipped. You must restart SQL Server for the certificate binding to take effect." + Write-Information "To restart: Restart-Service -Name '$serviceName' -Force" } - - Write-Information "Certificate binding completed for instance $InstanceName." + + # ============================================================ + # SUCCESS + # ============================================================ + Write-Information "==========================================" + Write-Information "Certificate binding completed successfully!" + Write-Information "Instance: $InstanceName" + Write-Information "Certificate: $NewThumbprint" + Write-Information "Service Account: $SqlServiceUser" + Write-Information "Key Storage: $keyStorageType" + Write-Information "ACL Method: $aclMethod" + + if ($RestartService) { + Write-Information "Service Status: Restarted" + } else { + Write-Information "Service Status: Restart Required" + } + Write-Information "==========================================" + + return $true } catch { - Write-Error "An error occurred: $_" + Write-Error "Certificate binding failed for instance $InstanceName" + Write-Error "Error: $_" + Write-Verbose "Stack trace: $($_.ScriptStackTrace)" return $false } - - return $true } function Unbind-KFSqlCertificate { @@ -1464,6 +1964,15 @@ function Parse-DNSubject { return $subjectString } +function Test-ValidSslFlags { + param([int]$SslFlags) + + $validBits = 1,2,4,8,32,64,128 + $invalidBits = $SslFlags -bxor ($SslFlags -band ($validBits | Measure-Object -Sum).Sum) + + return ($invalidBits -eq 0) +} + # Note: Removed Test-IISBindingConflict function - we now mimic IIS behavior # IIS replaces exact matches and allows multiple hostnames (SNI) on same IP:Port function Get-IISManagementInfo { diff --git a/README.md b/README.md index 95cf76d..2a1341c 100644 --- a/README.md +++ b/README.md @@ -280,6 +280,57 @@ the Keyfactor Command Portal ![WinCert Custom Fields Tab](docsource/images/WinCert-custom-fields-store-type-dialog.png) + + ###### SPN With Port + Internally set the -IncludePortInSPN option when creating the remote PowerShell connection. Needed for some Kerberos configurations. + + ![WinCert Custom Field - spnwithport](docsource/images/WinCert-custom-field-spnwithport-dialog.png) + + + + ###### WinRM Protocol + Multiple choice value specifying which protocol to use. Protocols https or http use WinRM to connect from Windows to Windows Servers. Using ssh is only supported when running the orchestrator in a Linux environment. + + ![WinCert Custom Field - WinRM Protocol](docsource/images/WinCert-custom-field-WinRM Protocol-dialog.png) + + + + ###### WinRM Port + String value specifying the port number that the Windows target server's WinRM listener is configured to use. Example: '5986' for HTTPS or '5985' for HTTP. By default, when using ssh in a Linux environment, the default port number is 22. + + ![WinCert Custom Field - WinRM Port](docsource/images/WinCert-custom-field-WinRM Port-dialog.png) + + + + ###### Server Username + Username used to log into the target server for establishing the WinRM session. Example: 'administrator' or 'domain\username'. + + + > [!IMPORTANT] + > This field is created by the `Needs Server` on the Basic tab, do not create this field manually. + + + + + ###### Server Password + Password corresponding to the Server Username used to log into the target server. When establishing a SSH session from a Linux environment, the password must include the full SSH Private key. + + + > [!IMPORTANT] + > This field is created by the `Needs Server` on the Basic tab, do not create this field manually. + + + + + ###### Use SSL + Determine whether the server uses SSL or not (This field is automatically created) + + ![WinCert Custom Field - ServerUseSsl](docsource/images/WinCert-custom-field-ServerUseSsl-dialog.png) + + + + + ##### Entry Parameters Tab | Name | Display Name | Description | Type | Default Value | Entry has a private key | Adding an entry | Removing an entry | Reenrolling an entry | @@ -291,6 +342,20 @@ the Keyfactor Command Portal ![WinCert Entry Parameters Tab](docsource/images/WinCert-entry-parameters-store-type-dialog.png) + + ##### Crypto Provider Name + Name of the Windows cryptographic service provider to use when generating and storing private keys. For more information, refer to the section 'Using Crypto Service Providers' + + ![WinCert Entry Parameter - ProviderName](docsource/images/WinCert-entry-parameters-store-type-dialog-ProviderName.png) + + + ##### SAN + String value specifying the Subject Alternative Name (SAN) to be used when performing reenrollment jobs. Format as a list of = entries separated by ampersands; Example: 'dns=www.example.com&dns=www.example2.com' for multiple SANs. Can be made optional if RFC 2818 is disabled on the CA. + + ![WinCert Entry Parameter - SAN](docsource/images/WinCert-entry-parameters-store-type-dialog-SAN.png) + + + @@ -409,6 +474,57 @@ the Keyfactor Command Portal ![IISU Custom Fields Tab](docsource/images/IISU-custom-fields-store-type-dialog.png) + + ###### SPN With Port + Internally set the -IncludePortInSPN option when creating the remote PowerShell connection. Needed for some Kerberos configurations. + + ![IISU Custom Field - spnwithport](docsource/images/IISU-custom-field-spnwithport-dialog.png) + + + + ###### WinRM Protocol + Multiple choice value specifying which protocol to use. Protocols https or http use WinRM to connect from Windows to Windows Servers. Using ssh is only supported when running the orchestrator in a Linux environment. + + ![IISU Custom Field - WinRM Protocol](docsource/images/IISU-custom-field-WinRM Protocol-dialog.png) + + + + ###### WinRM Port + String value specifying the port number that the Windows target server's WinRM listener is configured to use. Example: '5986' for HTTPS or '5985' for HTTP. By default, when using ssh in a Linux environment, the default port number is 22. + + ![IISU Custom Field - WinRM Port](docsource/images/IISU-custom-field-WinRM Port-dialog.png) + + + + ###### Server Username + Username used to log into the target server for establishing the WinRM session. Example: 'administrator' or 'domain\username'. + + + > [!IMPORTANT] + > This field is created by the `Needs Server` on the Basic tab, do not create this field manually. + + + + + ###### Server Password + Password corresponding to the Server Username used to log into the target server. When establishing a SSH session from a Linux environment, the password must include the full SSH Private key. + + + > [!IMPORTANT] + > This field is created by the `Needs Server` on the Basic tab, do not create this field manually. + + + + + ###### Use SSL + Determine whether the server uses SSL or not (This field is automatically created) + + ![IISU Custom Field - ServerUseSsl](docsource/images/IISU-custom-field-ServerUseSsl-dialog.png) + + + + + ##### Entry Parameters Tab | Name | Display Name | Description | Type | Default Value | Entry has a private key | Adding an entry | Removing an entry | Reenrolling an entry | @@ -426,6 +542,56 @@ the Keyfactor Command Portal ![IISU Entry Parameters Tab](docsource/images/IISU-entry-parameters-store-type-dialog.png) + + ##### Port + String value specifying the IP port to bind the certificate to for the IIS site. Example: '443' for HTTPS. + + ![IISU Entry Parameter - Port](docsource/images/IISU-entry-parameters-store-type-dialog-Port.png) + + + ##### IP Address + String value specifying the IP address to bind the certificate to for the IIS site. Example: '*' for all IP addresses or '192.168.1.1' for a specific IP address. + + ![IISU Entry Parameter - IPAddress](docsource/images/IISU-entry-parameters-store-type-dialog-IPAddress.png) + + + ##### Host Name + String value specifying the host name (host header) to bind the certificate to for the IIS site. Leave blank for all host names or enter a specific hostname such as 'www.example.com'. + + ![IISU Entry Parameter - HostName](docsource/images/IISU-entry-parameters-store-type-dialog-HostName.png) + + + ##### IIS Site Name + String value specifying the name of the IIS web site to bind the certificate to. Example: 'Default Web Site' or any custom site name such as 'MyWebsite'. + + ![IISU Entry Parameter - SiteName](docsource/images/IISU-entry-parameters-store-type-dialog-SiteName.png) + + + ##### SSL Flags + A 128-Bit Flag that determines what type of SSL settings you wish to use. The default is 0, meaning No SNI. For more information, check IIS documentation for the appropriate bit setting.) + + ![IISU Entry Parameter - SniFlag](docsource/images/IISU-entry-parameters-store-type-dialog-SniFlag.png) + + + ##### Protocol + Multiple choice value specifying the protocol to bind to. Example: 'https' for secure communication. + + ![IISU Entry Parameter - Protocol](docsource/images/IISU-entry-parameters-store-type-dialog-Protocol.png) + + + ##### Crypto Provider Name + Name of the Windows cryptographic service provider to use when generating and storing private keys. For more information, refer to the section 'Using Crypto Service Providers' + + ![IISU Entry Parameter - ProviderName](docsource/images/IISU-entry-parameters-store-type-dialog-ProviderName.png) + + + ##### SAN + String value specifying the Subject Alternative Name (SAN) to be used when performing reenrollment jobs. Format as a list of = entries separated by ampersands; Example: 'dns=www.example.com&dns=www.example2.com' for multiple SANs. Can be made optional if RFC 2818 is disabled on the CA. + + ![IISU Entry Parameter - SAN](docsource/images/IISU-entry-parameters-store-type-dialog-SAN.png) + + + @@ -537,6 +703,64 @@ the Keyfactor Command Portal ![WinSql Custom Fields Tab](docsource/images/WinSql-custom-fields-store-type-dialog.png) + + ###### SPN With Port + Internally set the -IncludePortInSPN option when creating the remote PowerShell connection. Needed for some Kerberos configurations. + + ![WinSql Custom Field - spnwithport](docsource/images/WinSql-custom-field-spnwithport-dialog.png) + + + + ###### WinRM Protocol + Multiple choice value specifying which protocol to use. Protocols https or http use WinRM to connect from Windows to Windows Servers. Using ssh is only supported when running the orchestrator in a Linux environment. + + ![WinSql Custom Field - WinRM Protocol](docsource/images/WinSql-custom-field-WinRM Protocol-dialog.png) + + + + ###### WinRM Port + String value specifying the port number that the Windows target server's WinRM listener is configured to use. Example: '5986' for HTTPS or '5985' for HTTP. By default, when using ssh in a Linux environment, the default port number is 22. + + ![WinSql Custom Field - WinRM Port](docsource/images/WinSql-custom-field-WinRM Port-dialog.png) + + + + ###### Server Username + Username used to log into the target server for establishing the WinRM session. Example: 'administrator' or 'domain\username'. + + + > [!IMPORTANT] + > This field is created by the `Needs Server` on the Basic tab, do not create this field manually. + + + + + ###### Server Password + Password corresponding to the Server Username used to log into the target server. When establishing a SSH session from a Linux environment, the password must include the full SSH Private key. + + + > [!IMPORTANT] + > This field is created by the `Needs Server` on the Basic tab, do not create this field manually. + + + + + ###### Use SSL + Determine whether the server uses SSL or not (This field is automatically created) + + ![WinSql Custom Field - ServerUseSsl](docsource/images/WinSql-custom-field-ServerUseSsl-dialog.png) + + + + ###### Restart SQL Service After Cert Installed + Boolean value (true or false) indicating whether to restart the SQL Server service after installing the certificate. Example: 'true' to enable service restart after installation. + + ![WinSql Custom Field - RestartService](docsource/images/WinSql-custom-field-RestartService-dialog.png) + + + + + ##### Entry Parameters Tab | Name | Display Name | Description | Type | Default Value | Entry has a private key | Adding an entry | Removing an entry | Reenrolling an entry | @@ -549,6 +773,26 @@ the Keyfactor Command Portal ![WinSql Entry Parameters Tab](docsource/images/WinSql-entry-parameters-store-type-dialog.png) + + ##### Instance Name + String value specifying the SQL Server instance name to bind the certificate to. Example: 'MSSQLServer' for the default instance or 'Instance1' for a named instance. + + ![WinSql Entry Parameter - InstanceName](docsource/images/WinSql-entry-parameters-store-type-dialog-InstanceName.png) + + + ##### Crypto Provider Name + Name of the Windows cryptographic service provider to use when generating and storing private keys. For more information, refer to the section 'Using Crypto Service Providers' + + ![WinSql Entry Parameter - ProviderName](docsource/images/WinSql-entry-parameters-store-type-dialog-ProviderName.png) + + + ##### SAN + String value specifying the Subject Alternative Name (SAN) to be used when performing reenrollment jobs. Format as a list of = entries separated by ampersands; Example: 'dns=www.example.com&dns=www.example2.com' for multiple SANs. + + ![WinSql Entry Parameter - SAN](docsource/images/WinSql-entry-parameters-store-type-dialog-SAN.png) + + + @@ -557,15 +801,14 @@ the Keyfactor Command Portal 1. **Download the latest Windows Certificate Universal Orchestrator extension from GitHub.** - Navigate to the [Windows Certificate Universal Orchestrator extension GitHub version page](https://github.com/Keyfactor/iis-orchestrator/releases/latest). Refer to the compatibility matrix below to determine whether the `net6.0` or `net8.0` asset should be downloaded. Then, click the corresponding asset to download the zip archive. + Navigate to the [Windows Certificate Universal Orchestrator extension GitHub version page](https://github.com/Keyfactor/iis-orchestrator/releases/latest). Refer to the compatibility matrix below to determine the asset should be downloaded. Then, click the corresponding asset to download the zip archive. | Universal Orchestrator Version | Latest .NET version installed on the Universal Orchestrator server | `rollForward` condition in `Orchestrator.runtimeconfig.json` | `iis-orchestrator` .NET version to download | | --------- | ----------- | ----------- | ----------- | | Older than `11.0.0` | | | `net6.0` | | Between `11.0.0` and `11.5.1` (inclusive) | `net6.0` | | `net6.0` | - | Between `11.0.0` and `11.5.1` (inclusive) | `net8.0` | `Disable` | `net6.0` | - | Between `11.0.0` and `11.5.1` (inclusive) | `net8.0` | `LatestMajor` | `net8.0` | - | `11.6` _and_ newer | `net8.0` | | `net8.0` | + | Between `11.0.0` and `11.5.1` (inclusive) | `net8.0` | `Disable` | `net6.0` || Between `11.0.0` and `11.5.1` (inclusive) | `net8.0` | `LatestMajor` | `net8.0` | + | `11.6` _and_ newer | `net8.0` | | `net8.0` | Unzip the archive containing extension assemblies to a known location. diff --git a/docsource/images/IISU-advanced-store-type-dialog.png b/docsource/images/IISU-advanced-store-type-dialog.png index eab1385..2d6fca8 100644 Binary files a/docsource/images/IISU-advanced-store-type-dialog.png and b/docsource/images/IISU-advanced-store-type-dialog.png differ diff --git a/docsource/images/IISU-basic-store-type-dialog.png b/docsource/images/IISU-basic-store-type-dialog.png index 17be4a5..fa4e029 100644 Binary files a/docsource/images/IISU-basic-store-type-dialog.png and b/docsource/images/IISU-basic-store-type-dialog.png differ diff --git a/docsource/images/IISU-custom-field-ServerUseSsl-dialog.png b/docsource/images/IISU-custom-field-ServerUseSsl-dialog.png new file mode 100644 index 0000000..8b3b949 Binary files /dev/null and b/docsource/images/IISU-custom-field-ServerUseSsl-dialog.png differ diff --git a/docsource/images/IISU-custom-field-WinRMPort-dialog.png b/docsource/images/IISU-custom-field-WinRMPort-dialog.png new file mode 100644 index 0000000..772e8d5 Binary files /dev/null and b/docsource/images/IISU-custom-field-WinRMPort-dialog.png differ diff --git a/docsource/images/IISU-custom-field-WinRMProtocol-dialog.png b/docsource/images/IISU-custom-field-WinRMProtocol-dialog.png new file mode 100644 index 0000000..72b2a94 Binary files /dev/null and b/docsource/images/IISU-custom-field-WinRMProtocol-dialog.png differ diff --git a/docsource/images/IISU-custom-field-spnwithport-dialog.png b/docsource/images/IISU-custom-field-spnwithport-dialog.png new file mode 100644 index 0000000..543b076 Binary files /dev/null and b/docsource/images/IISU-custom-field-spnwithport-dialog.png differ diff --git a/docsource/images/IISU-entry-parameters-store-type-dialog-HostName.png b/docsource/images/IISU-entry-parameters-store-type-dialog-HostName.png new file mode 100644 index 0000000..4db52c1 Binary files /dev/null and b/docsource/images/IISU-entry-parameters-store-type-dialog-HostName.png differ diff --git a/docsource/images/IISU-entry-parameters-store-type-dialog-IPAddress.png b/docsource/images/IISU-entry-parameters-store-type-dialog-IPAddress.png new file mode 100644 index 0000000..3ca535d Binary files /dev/null and b/docsource/images/IISU-entry-parameters-store-type-dialog-IPAddress.png differ diff --git a/docsource/images/IISU-entry-parameters-store-type-dialog-Port.png b/docsource/images/IISU-entry-parameters-store-type-dialog-Port.png new file mode 100644 index 0000000..0ef992b Binary files /dev/null and b/docsource/images/IISU-entry-parameters-store-type-dialog-Port.png differ diff --git a/docsource/images/IISU-entry-parameters-store-type-dialog-Protocol.png b/docsource/images/IISU-entry-parameters-store-type-dialog-Protocol.png new file mode 100644 index 0000000..e23c4d3 Binary files /dev/null and b/docsource/images/IISU-entry-parameters-store-type-dialog-Protocol.png differ diff --git a/docsource/images/IISU-entry-parameters-store-type-dialog-ProviderName.png b/docsource/images/IISU-entry-parameters-store-type-dialog-ProviderName.png new file mode 100644 index 0000000..9598bd5 Binary files /dev/null and b/docsource/images/IISU-entry-parameters-store-type-dialog-ProviderName.png differ diff --git a/docsource/images/IISU-entry-parameters-store-type-dialog-SAN.png b/docsource/images/IISU-entry-parameters-store-type-dialog-SAN.png new file mode 100644 index 0000000..9aaf92a Binary files /dev/null and b/docsource/images/IISU-entry-parameters-store-type-dialog-SAN.png differ diff --git a/docsource/images/IISU-entry-parameters-store-type-dialog-SiteName.png b/docsource/images/IISU-entry-parameters-store-type-dialog-SiteName.png new file mode 100644 index 0000000..0fc1a73 Binary files /dev/null and b/docsource/images/IISU-entry-parameters-store-type-dialog-SiteName.png differ diff --git a/docsource/images/IISU-entry-parameters-store-type-dialog-SniFlag.png b/docsource/images/IISU-entry-parameters-store-type-dialog-SniFlag.png new file mode 100644 index 0000000..616108b Binary files /dev/null and b/docsource/images/IISU-entry-parameters-store-type-dialog-SniFlag.png differ diff --git a/docsource/images/IISU-entry-parameters-store-type-dialog.png b/docsource/images/IISU-entry-parameters-store-type-dialog.png index e8d8817..4a8a538 100644 Binary files a/docsource/images/IISU-entry-parameters-store-type-dialog.png and b/docsource/images/IISU-entry-parameters-store-type-dialog.png differ diff --git a/docsource/images/WinCert-advanced-store-type-dialog.png b/docsource/images/WinCert-advanced-store-type-dialog.png index 8b43572..9000147 100644 Binary files a/docsource/images/WinCert-advanced-store-type-dialog.png and b/docsource/images/WinCert-advanced-store-type-dialog.png differ diff --git a/docsource/images/WinCert-basic-store-type-dialog.png b/docsource/images/WinCert-basic-store-type-dialog.png index 9f18381..75e8360 100644 Binary files a/docsource/images/WinCert-basic-store-type-dialog.png and b/docsource/images/WinCert-basic-store-type-dialog.png differ diff --git a/docsource/images/WinCert-custom-field-ServerUseSsl-dialog.png b/docsource/images/WinCert-custom-field-ServerUseSsl-dialog.png new file mode 100644 index 0000000..8b3b949 Binary files /dev/null and b/docsource/images/WinCert-custom-field-ServerUseSsl-dialog.png differ diff --git a/docsource/images/WinCert-custom-field-WinRMPort-dialog.png b/docsource/images/WinCert-custom-field-WinRMPort-dialog.png new file mode 100644 index 0000000..772e8d5 Binary files /dev/null and b/docsource/images/WinCert-custom-field-WinRMPort-dialog.png differ diff --git a/docsource/images/WinCert-custom-field-WinRMProtocol-dialog.png b/docsource/images/WinCert-custom-field-WinRMProtocol-dialog.png new file mode 100644 index 0000000..72b2a94 Binary files /dev/null and b/docsource/images/WinCert-custom-field-WinRMProtocol-dialog.png differ diff --git a/docsource/images/WinCert-custom-field-spnwithport-dialog.png b/docsource/images/WinCert-custom-field-spnwithport-dialog.png new file mode 100644 index 0000000..543b076 Binary files /dev/null and b/docsource/images/WinCert-custom-field-spnwithport-dialog.png differ diff --git a/docsource/images/WinCert-entry-parameters-store-type-dialog-ProviderName.png b/docsource/images/WinCert-entry-parameters-store-type-dialog-ProviderName.png new file mode 100644 index 0000000..9cde9d3 Binary files /dev/null and b/docsource/images/WinCert-entry-parameters-store-type-dialog-ProviderName.png differ diff --git a/docsource/images/WinCert-entry-parameters-store-type-dialog-SAN.png b/docsource/images/WinCert-entry-parameters-store-type-dialog-SAN.png new file mode 100644 index 0000000..d97892a Binary files /dev/null and b/docsource/images/WinCert-entry-parameters-store-type-dialog-SAN.png differ diff --git a/docsource/images/WinSql-advanced-store-type-dialog.png b/docsource/images/WinSql-advanced-store-type-dialog.png index 8b43572..9000147 100644 Binary files a/docsource/images/WinSql-advanced-store-type-dialog.png and b/docsource/images/WinSql-advanced-store-type-dialog.png differ diff --git a/docsource/images/WinSql-custom-field-RestartService-dialog.png b/docsource/images/WinSql-custom-field-RestartService-dialog.png new file mode 100644 index 0000000..704a523 Binary files /dev/null and b/docsource/images/WinSql-custom-field-RestartService-dialog.png differ diff --git a/docsource/images/WinSql-custom-field-ServerUseSsl-dialog.png b/docsource/images/WinSql-custom-field-ServerUseSsl-dialog.png new file mode 100644 index 0000000..8b3b949 Binary files /dev/null and b/docsource/images/WinSql-custom-field-ServerUseSsl-dialog.png differ diff --git a/docsource/images/WinSql-custom-field-WinRMPort-dialog.png b/docsource/images/WinSql-custom-field-WinRMPort-dialog.png new file mode 100644 index 0000000..772e8d5 Binary files /dev/null and b/docsource/images/WinSql-custom-field-WinRMPort-dialog.png differ diff --git a/docsource/images/WinSql-custom-field-WinRMProtocol-dialog.png b/docsource/images/WinSql-custom-field-WinRMProtocol-dialog.png new file mode 100644 index 0000000..72b2a94 Binary files /dev/null and b/docsource/images/WinSql-custom-field-WinRMProtocol-dialog.png differ diff --git a/docsource/images/WinSql-custom-field-spnwithport-dialog.png b/docsource/images/WinSql-custom-field-spnwithport-dialog.png new file mode 100644 index 0000000..543b076 Binary files /dev/null and b/docsource/images/WinSql-custom-field-spnwithport-dialog.png differ diff --git a/docsource/images/WinSql-entry-parameters-store-type-dialog-InstanceName.png b/docsource/images/WinSql-entry-parameters-store-type-dialog-InstanceName.png new file mode 100644 index 0000000..49f27bf Binary files /dev/null and b/docsource/images/WinSql-entry-parameters-store-type-dialog-InstanceName.png differ diff --git a/docsource/images/WinSql-entry-parameters-store-type-dialog-ProviderName.png b/docsource/images/WinSql-entry-parameters-store-type-dialog-ProviderName.png new file mode 100644 index 0000000..b5287bb Binary files /dev/null and b/docsource/images/WinSql-entry-parameters-store-type-dialog-ProviderName.png differ diff --git a/docsource/images/WinSql-entry-parameters-store-type-dialog-SAN.png b/docsource/images/WinSql-entry-parameters-store-type-dialog-SAN.png new file mode 100644 index 0000000..ea23ade Binary files /dev/null and b/docsource/images/WinSql-entry-parameters-store-type-dialog-SAN.png differ diff --git a/docsource/images/WinSql-entry-parameters-store-type-dialog.png b/docsource/images/WinSql-entry-parameters-store-type-dialog.png index 7dd632d..178f842 100644 Binary files a/docsource/images/WinSql-entry-parameters-store-type-dialog.png and b/docsource/images/WinSql-entry-parameters-store-type-dialog.png differ