diff --git a/CHANGELOG.md b/CHANGELOG.md
index a26d151..0a3ff8d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,7 @@
-2.5.1
-* Fixed WinSQL service name when InstanceID differs from InstanceName
+2.6.0
+* Added the ability to run the extension in a Linux environment. To utilize this change, for each Cert Store Types (WinCert/WinIIS/WinSQL), add ssh to the Custom Field WinRM Protocol. When using ssh as a protocol, make sure to enter the appropriate ssh port number under WinRM Port.
+* NOTE: For legacy purposes the Display names WinRM Protocol and WinRM Port are maintained although the type of protocols now includes ssh.
+* Moved all inventory and management jobs to external PowerShell script file .\PowerShellScripts\WinCertScripts.ps1
2.5.0
* Added the Bindings to the end of the thumbprint to make the alias unique.
diff --git a/IISU/Certificate.cs b/IISU/Certificate.cs
index 81b89a4..f8d28c0 100644
--- a/IISU/Certificate.cs
+++ b/IISU/Certificate.cs
@@ -12,9 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// 021225 rcp 2.6.0 Cleaned up and verified code
+
+using Newtonsoft.Json;
using System;
-using System.Linq;
-using System.Text.RegularExpressions;
+using System.Collections.Generic;
namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore
{
@@ -29,30 +31,27 @@ public class Certificate
public class Utilities
{
- public static string FormatSAN(string san)
+ public static List DeserializeCertificates(string jsonResults)
{
- // Use regular expression to extract key-value pairs
- var regex = new Regex(@"(?DNS Name|Email|IP Address)=(?[^=,\s]+)");
- var matches = regex.Matches(san);
-
- // Format matches into the desired format
- string result = string.Join("&", matches.Cast()
- .Select(m => $"{NormalizeKey(m.Groups["key"].Value)}={m.Groups["value"].Value}"));
-
- return result;
- }
+ if (string.IsNullOrEmpty(jsonResults))
+ {
+ // Handle no objects returned
+ return new List();
+ }
- private static string NormalizeKey(string key)
- {
- return key.ToLower() switch
+ // Determine if the JSON is an array or a single object
+ if (jsonResults.TrimStart().StartsWith("["))
{
- "dns name" => "dns",
- "email" => "email",
- "ip address" => "ip",
- _ => key.ToLower() // For other types, keep them as-is
- };
+ // It's an array, deserialize as list
+ return JsonConvert.DeserializeObject>(jsonResults);
+ }
+ else
+ {
+ // It's a single object, wrap it in a list
+ var singleObject = JsonConvert.DeserializeObject(jsonResults);
+ return new List { singleObject };
+ }
}
-
}
}
}
\ No newline at end of file
diff --git a/IISU/CertificateStore.cs b/IISU/CertificateStore.cs
deleted file mode 100644
index 75c1d0a..0000000
--- a/IISU/CertificateStore.cs
+++ /dev/null
@@ -1,211 +0,0 @@
-// Copyright 2022 Keyfactor
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-using Keyfactor.Orchestrators.Common.Enums;
-using Keyfactor.Orchestrators.Extensions;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Management.Automation;
-using System.Management.Automation.Runspaces;
-
-namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore
-{
- public class CertificateStore
- {
- public CertificateStore(string serverName, string storePath, Runspace runSpace)
- {
- ServerName = serverName;
- StorePath = storePath;
- RunSpace = runSpace;
- Initalize();
- }
-
- public string ServerName { get; set; }
- public string StorePath { get; set; }
- public Runspace RunSpace { get; set; }
- public List Certificates { get; set; }
-
- public void RemoveCertificate(string thumbprint)
- {
- using var ps = PowerShell.Create();
- ps.Runspace = RunSpace;
-
- // Open with value of 5 means: Open existing only (4) + Open ReadWrite (1)
- var removeScript = $@"
- $ErrorActionPreference = 'Stop'
- $certStore = New-Object System.Security.Cryptography.X509Certificates.X509Store('{StorePath}','LocalMachine')
- $certStore.Open(5)
- $certToRemove = $certStore.Certificates.Find(0,'{thumbprint}',$false)
- if($certToRemove.Count -gt 0) {{
- $certStore.Remove($certToRemove[0])
- }}
- $certStore.Close()
- $certStore.Dispose()
- ";
-
- ps.AddScript(removeScript);
-
- var _ = ps.Invoke();
- if (ps.HadErrors)
- throw new CertificateStoreException($"Error removing certificate in {StorePath} store on {ServerName}.");
- }
-
- private void Initalize()
- {
- Certificates = new List();
- try
- {
- using var ps = PowerShell.Create();
- ps.Runspace = RunSpace;
-
- var certStoreScript = $@"
- $certStore = New-Object System.Security.Cryptography.X509Certificates.X509Store('{StorePath}','LocalMachine')
- $certStore.Open('ReadOnly')
- $certs = $certStore.Certificates
- $certStore.Close()
- $certStore.Dispose()
- foreach ( $cert in $certs){{
- $cert | Select-Object -Property Thumbprint, RawData, HasPrivateKey
- }}";
-
- ps.AddScript(certStoreScript);
-
- var certs = ps.Invoke();
-
- foreach (var c in certs)
- Certificates.Add(new Certificate
- {
- Thumbprint = $"{c.Properties["Thumbprint"]?.Value}",
- HasPrivateKey = bool.Parse($"{c.Properties["HasPrivateKey"]?.Value}"),
- RawData = (byte[])c.Properties["RawData"]?.Value
- });
- }
- catch (Exception ex)
- {
- throw new CertificateStoreException(
- $"Error listing certificate in {StorePath} store on {ServerName}: {ex.Message}");
- }
- }
-
- private static List PerformGetCertificateInvenotory(Runspace runSpace, string storePath)
- {
- List myCertificates = new List();
- try
- {
- using var ps = PowerShell.Create();
- ps.Runspace = runSpace;
-
- var certStoreScript = $@"
- $certStore = New-Object System.Security.Cryptography.X509Certificates.X509Store('{storePath}','LocalMachine')
- $certStore.Open('ReadOnly')
- $certs = $certStore.Certificates
- $certStore.Close()
- $certStore.Dispose()
- foreach ( $cert in $certs){{
- $cert | Select-Object -Property Thumbprint, RawData, HasPrivateKey
- }}";
-
- ps.AddScript(certStoreScript);
-
- var certs = ps.Invoke();
-
- foreach (var c in certs)
- myCertificates.Add(new Certificate
- {
- Thumbprint = $"{c.Properties["Thumbprint"]?.Value}",
- HasPrivateKey = bool.Parse($"{c.Properties["HasPrivateKey"]?.Value}"),
- RawData = (byte[])c.Properties["RawData"]?.Value
- });
-
- return myCertificates;
- }
- catch (Exception ex)
- {
- throw new CertificateStoreException(
- $"Error listing certificate in {storePath} store on {runSpace.ConnectionInfo.ComputerName}: {ex.Message}");
- }
-
- }
-
- public static List GetCertificatesFromStore(Runspace runSpace, string storePath)
- {
- return PerformGetCertificateInvenotory(runSpace, storePath);
- }
-
- public static List GetIISBoundCertificates(Runspace runSpace, string storePath)
- {
- List myCertificates = PerformGetCertificateInvenotory(runSpace, storePath);
- List myBoundCerts = new List();
-
- using (var ps = PowerShell.Create())
- {
- ps.Runspace = runSpace;
-
- ps.AddCommand("Import-Module")
- .AddParameter("Name", "WebAdministration")
- .AddStatement();
-
- var searchScript = "Foreach($Site in get-website) { Foreach ($Bind in $Site.bindings.collection) {[pscustomobject]@{name=$Site.name;Protocol=$Bind.Protocol;Bindings=$Bind.BindingInformation;thumbprint=$Bind.certificateHash;sniFlg=$Bind.sslFlags}}}";
- ps.AddScript(searchScript).AddStatement();
- var iisBindings = ps.Invoke(); // Responsible for getting all bound certificates for each website
-
- if (ps.HadErrors)
- {
- var psError = ps.Streams.Error.ReadAll().Aggregate(string.Empty, (current, error) => current + error.ErrorDetails.Message);
- }
-
- if (iisBindings.Count == 0)
- {
- return myBoundCerts;
- }
-
- //in theory should only be one, but keeping for future update to chance inventory
- foreach (var binding in iisBindings)
- {
- var thumbPrint = $"{binding.Properties["thumbprint"]?.Value}";
- if (string.IsNullOrEmpty(thumbPrint)) continue;
-
- Certificate foundCert = myCertificates.Find(m => m.Thumbprint.Equals(thumbPrint));
-
- if (foundCert == null) continue;
-
- var siteSettingsDict = new Dictionary
- {
- { "SiteName", binding.Properties["Name"]?.Value },
- { "Port", binding.Properties["Bindings"]?.Value.ToString()?.Split(':')[1] },
- { "IPAddress", binding.Properties["Bindings"]?.Value.ToString()?.Split(':')[0] },
- { "HostName", binding.Properties["Bindings"]?.Value.ToString()?.Split(':')[2] },
- { "SniFlag", binding.Properties["sniFlg"]?.Value },
- { "Protocol", binding.Properties["Protocol"]?.Value }
- };
-
- myBoundCerts.Add(
- new CurrentInventoryItem
- {
- Certificates = new[] { foundCert.CertificateData },
- Alias = thumbPrint + ":" + binding.Properties["Bindings"]?.Value.ToString(),
- PrivateKeyEntry = foundCert.HasPrivateKey,
- UseChainLevel = false,
- ItemStatus = OrchestratorInventoryItemStatus.Unknown,
- Parameters = siteSettingsDict
- }
- );
- }
-
- return myBoundCerts;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/IISU/ClientPSCertStoreInventory.cs b/IISU/ClientPSCertStoreInventory.cs
deleted file mode 100644
index b3b85e1..0000000
--- a/IISU/ClientPSCertStoreInventory.cs
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2022 Keyfactor
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-using Keyfactor.Extensions.Orchestrator.WindowsCertStore.IISU;
-using Keyfactor.Logging;
-using Microsoft.Extensions.Logging;
-using System;
-using System.Collections.Generic;
-using System.Management.Automation;
-using System.Management.Automation.Runspaces;
-using System.Text;
-
-namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore
-{
- public abstract class ClientPSCertStoreInventory
- {
- private ILogger _logger;
-
- protected ClientPSCertStoreInventory()
- {
- _logger = LogHandler.GetClassLogger();
- }
-
- public ClientPSCertStoreInventory(ILogger logger)
- {
- _logger = logger;
- }
-
- public List GetCertificatesFromStore(Runspace runSpace, string storePath)
- {
- List myCertificates = new List();
- try
- {
- using var ps = PowerShell.Create();
-
- _logger.MethodEntry();
-
- ps.Runspace = runSpace;
-
- var certStoreScript = $@"
- $certStore = New-Object System.Security.Cryptography.X509Certificates.X509Store('{storePath}','LocalMachine')
- $certStore.Open('ReadOnly')
- $certs = $certStore.Certificates
- $certStore.Close()
- $certStore.Dispose()
- $certs | ForEach-Object {{
- $certDetails = @{{
- Subject = $_.Subject
- Thumbprint = $_.Thumbprint
- HasPrivateKey = $_.HasPrivateKey
- RawData = $_.RawData
- san = $_.Extensions | Where-Object {{ $_.Oid.FriendlyName -eq ""Subject Alternative Name"" }} | ForEach-Object {{ $_.Format($false) }}
- }}
-
- if ($_.HasPrivateKey) {{
- $certDetails.CSP = $_.PrivateKey.CspKeyContainerInfo.ProviderName
- }}
-
- New-Object PSObject -Property $certDetails
- }}";
-
- ps.AddScript(certStoreScript);
-
- _logger.LogTrace($"Executing the following script:\n{certStoreScript}");
-
- var certs = ps.Invoke();
-
- foreach (var c in certs)
- {
- myCertificates.Add(new Certificate
- {
- Thumbprint = $"{c.Properties["Thumbprint"]?.Value}",
- HasPrivateKey = bool.Parse($"{c.Properties["HasPrivateKey"]?.Value}"),
- RawData = (byte[])c.Properties["RawData"]?.Value,
- CryptoServiceProvider = $"{c.Properties["CSP"]?.Value }",
- SAN = Certificate.Utilities.FormatSAN($"{c.Properties["san"]?.Value}")
- });
- }
- _logger.LogTrace($"found: {myCertificates.Count} certificate(s), exiting GetCertificatesFromStore()");
- return myCertificates;
- }
- catch (Exception ex)
- {
- _logger.LogTrace($"An error occurred in the WinCert GetCertificatesFromStore method:\n{ex.Message}");
-
- throw new CertificateStoreException(
- $"Error listing certificate in {storePath} store on {runSpace.ConnectionInfo.ComputerName}: {ex.Message}");
- }
- }
- }
-}
diff --git a/IISU/ClientPSCertStoreManager.cs b/IISU/ClientPSCertStoreManager.cs
deleted file mode 100644
index e768f08..0000000
--- a/IISU/ClientPSCertStoreManager.cs
+++ /dev/null
@@ -1,367 +0,0 @@
-// Copyright 2022 Keyfactor
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-using Keyfactor.Logging;
-using Keyfactor.Orchestrators.Common.Enums;
-using Keyfactor.Orchestrators.Extensions;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.FlowAnalysis;
-using Microsoft.Extensions.Logging;
-using System;
-using System.IO;
-using System.Linq.Expressions;
-using System.Management.Automation;
-using System.Management.Automation.Runspaces;
-using System.Security.Cryptography.X509Certificates;
-using System.Text;
-
-namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore
-{
- public class ClientPSCertStoreManager
- {
- private ILogger _logger;
- private Runspace _runspace;
- private long _jobNumber = 0;
-
- private X509Certificate2 x509Cert;
-
- public X509Certificate2 X509Cert
- {
- get { return x509Cert; }
- }
-
- public ClientPSCertStoreManager(Runspace runSpace)
- {
- _logger = LogHandler.GetClassLogger();
- _runspace = runSpace;
- }
-
- public ClientPSCertStoreManager(ILogger logger, Runspace runSpace, long jobNumber)
- {
- _logger = logger;
- _runspace = runSpace;
- _jobNumber = jobNumber;
- }
-
- public string CreatePFXFile(string certificateContents, string privateKeyPassword)
- {
- _logger.LogTrace("Entering CreatePFXFile");
- if (!string.IsNullOrEmpty(privateKeyPassword)) { _logger.LogTrace("privateKeyPassword was present"); }
- else _logger.LogTrace("No privateKeyPassword Presented");
-
- try
- {
- // Create the x509 certificate
- x509Cert = new X509Certificate2
- (
- Convert.FromBase64String(certificateContents),
- privateKeyPassword,
- X509KeyStorageFlags.MachineKeySet |
- X509KeyStorageFlags.PersistKeySet |
- X509KeyStorageFlags.Exportable
- );
-
- using (PowerShell ps = PowerShell.Create())
- {
- ps.Runspace = _runspace;
-
- // Add script to write certificate contents to a temporary file
- string script = @"
- param($certificateContents)
- $filePath = [System.IO.Path]::GetTempFileName() + '.pfx'
- [System.IO.File]::WriteAllBytes($filePath, [System.Convert]::FromBase64String($certificateContents))
- $filePath
- ";
-
- ps.AddScript(script);
- ps.AddParameter("certificateContents", certificateContents); // Convert.ToBase64String(x509Cert.Export(X509ContentType.Pkcs12)));
-
- // Invoke the script on the remote computer
- var results = ps.Invoke();
-
- // Get the result (temporary file path) returned by the script
- _logger.LogTrace($"Results after creating PFX File: {results[0].ToString()}");
- return results[0].ToString();
- }
- }
- catch (Exception ex)
- {
- _logger.LogError(ex.ToString());
- throw new Exception("An error occurred while attempting to create and write the X509 contents.");
- }
- }
-
- public void DeletePFXFile(string filePath, string fileName)
- {
- using (PowerShell ps = PowerShell.Create())
- {
- ps.Runspace = _runspace;
-
- // Add script to delete the temporary file
- string deleteScript = @"
- param($filePath)
- Remove-Item -Path $filePath -Force
- ";
-
- ps.AddScript(deleteScript);
- ps.AddParameter("filePath", Path.Combine(filePath, fileName) + "*");
-
- // Invoke the script to delete the file
- var results = ps.Invoke();
- }
- }
-
- public JobResult ImportPFXFile(string filePath, string privateKeyPassword, string cryptoProviderName, string storePath)
- {
- try
- {
- _logger.LogTrace("Entering ImportPFX");
-
- using (PowerShell ps = PowerShell.Create())
- {
- ps.Runspace = _runspace;
-
- if (string.IsNullOrEmpty(cryptoProviderName))
- {
- if (string.IsNullOrEmpty(privateKeyPassword))
- {
- // If no private key password is provided, import the pfx file directory to the store using addstore argument
- string script = @"
- param($pfxFilePath, $storePath)
- $output = certutil -f -addstore $storePath $pfxFilePath 2>&1
- $exit_message = ""LASTEXITCODE:$($LASTEXITCODE)""
-
- if ($output.GetType().Name -eq ""String"")
- {
- $output = @($output, $exit_message)
- }
- else
- {
- $output += $exit_message
- }
- $output
- ";
-
- ps.AddScript(script);
- ps.AddParameter("pfxFilePath", filePath);
- ps.AddParameter("storePath", storePath);
- }
- else
- {
- // Use ImportPFX to import the pfx file with private key password to the appropriate cert store
-
- string script = @"
- param($pfxFilePath, $privateKeyPassword, $storePath)
- $output = certutil -f -importpfx -p $privateKeyPassword $storePath $pfxFilePath 2>&1
- $exit_message = ""LASTEXITCODE:$($LASTEXITCODE)""
-
- if ($output.GetType().Name -eq ""String"")
- {
- $output = @($output, $exit_message)
- }
- else
- {
- $output += $exit_message
- }
- $output
- ";
-
- ps.AddScript(script);
- ps.AddParameter("pfxFilePath", filePath);
- ps.AddParameter("privateKeyPassword", privateKeyPassword);
- ps.AddParameter("storePath", storePath);
- }
- }
- else
- {
- if (string.IsNullOrEmpty(privateKeyPassword))
- {
- string script = @"
- param($pfxFilePath, $cspName, $storePath)
- $output = certutil -f -csp $cspName -addstore $storePath $pfxFilePath 2>&1
- $exit_message = ""LASTEXITCODE:$($LASTEXITCODE)""
-
- if ($output.GetType().Name -eq ""String"")
- {
- $output = @($output, $exit_message)
- }
- else
- {
- $output += $exit_message
- }
- $output
- ";
-
- ps.AddScript(script);
- ps.AddParameter("pfxFilePath", filePath);
- ps.AddParameter("cspName", cryptoProviderName);
- ps.AddParameter("storePath", storePath);
- }
- else
- {
- string script = @"
- param($pfxFilePath, $privateKeyPassword, $cspName, $storePath)
- $output = certutil -f -importpfx -csp $cspName -p $privateKeyPassword $storePath $pfxFilePath 2>&1
- $exit_message = ""LASTEXITCODE:$($LASTEXITCODE)""
-
- if ($output.GetType().Name -eq ""String"")
- {
- $output = @($output, $exit_message)
- }
- else
- {
- $output += $exit_message
- }
- $output
- ";
-
- ps.AddScript(script);
- ps.AddParameter("pfxFilePath", filePath);
- ps.AddParameter("privateKeyPassword", privateKeyPassword);
- ps.AddParameter("cspName", cryptoProviderName);
- ps.AddParameter("storePath", storePath);
- }
- }
-
- // Invoke the script
- _logger.LogTrace("Attempting to import the PFX");
- var results = ps.Invoke();
-
- // Get the last exist code returned from the script
- int lastExitCode = 0;
- try
- {
- lastExitCode = GetLastExitCode(results[^1].ToString());
- _logger.LogTrace($"Last exit code: {lastExitCode}");
- }
- catch (Exception ex)
- {
- _logger.LogTrace(ex.Message);
- }
-
-
- bool isError = false;
- if (lastExitCode != 0)
- {
- isError = true;
- string outputMsg = "";
-
- foreach (var result in results)
- {
- string outputLine = result.ToString();
- if (!string.IsNullOrEmpty(outputLine))
- {
- outputMsg += "\n" + outputLine;
- }
- }
- _logger.LogError(outputMsg);
- }
- else
- {
- // Check for errors in the output
- foreach (var result in results)
- {
- string outputLine = result.ToString();
-
- _logger.LogTrace(outputLine);
-
- if (!string.IsNullOrEmpty(outputLine) && outputLine.Contains("Error") || outputLine.Contains("permissions are needed"))
- {
- isError = true;
- _logger.LogError(outputLine);
- }
- }
- }
-
- if (isError)
- {
- throw new Exception("Error occurred while attempting to import the pfx file.");
- }
- else
- {
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Success,
- JobHistoryId = _jobNumber,
- FailureMessage = ""
- };
- }
- }
- }
- catch (Exception e)
- {
- _logger.LogError($"Error Occurred in ClientPSCertStoreManager.ImportPFXFile(): {e.Message}");
-
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = _jobNumber,
- FailureMessage = $"Error Occurred in ImportPFXFile {LogHandler.FlattenException(e)}"
- };
- }
- }
-
- private int GetLastExitCode(string result)
- {
- // Split the string by colon
- string[] parts = result.Split(':');
-
- // Ensure the split result has the expected parts
- if (parts.Length == 2 && parts[0] == "LASTEXITCODE")
- {
- // Parse the second part into an integer
- if (int.TryParse(parts[1], out int lastExitCode))
- {
- return lastExitCode;
- }
- else
- {
- throw new Exception("Failed to parse the LASTEXITCODE value.");
- }
- }
- else
- {
- throw new Exception("The last element does not contain the expected format.");
- }
- }
-
- public void RemoveCertificate(string thumbprint, string storePath)
- {
- using var ps = PowerShell.Create();
-
- _logger.MethodEntry();
-
- ps.Runspace = _runspace;
-
- // Open with value of 5 means: Open existing only (4) + Open ReadWrite (1)
- var removeScript = $@"
- $ErrorActionPreference = 'Stop'
- $certStore = New-Object System.Security.Cryptography.X509Certificates.X509Store('{storePath}','LocalMachine')
- $certStore.Open(5)
- $certToRemove = $certStore.Certificates.Find(0,'{thumbprint}',$false)
- if($certToRemove.Count -gt 0) {{
- $certStore.Remove($certToRemove[0])
- }}
- $certStore.Close()
- $certStore.Dispose()
- ";
-
- ps.AddScript(removeScript);
-
- var _ = ps.Invoke();
- if (ps.HadErrors)
- throw new CertificateStoreException($"Error removing certificate in {storePath} store on {_runspace.ConnectionInfo.ComputerName}.");
-
- }
- }
-}
diff --git a/IISU/ClientPSCertStoreReEnrollment.cs b/IISU/ClientPSCertStoreReEnrollment.cs
index 85ce1ad..86dc118 100644
--- a/IISU/ClientPSCertStoreReEnrollment.cs
+++ b/IISU/ClientPSCertStoreReEnrollment.cs
@@ -1,297 +1,297 @@
-// Copyright 2022 Keyfactor
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-using Keyfactor.Logging;
-using Keyfactor.Orchestrators.Common.Enums;
-using Keyfactor.Orchestrators.Extensions;
-using Newtonsoft.Json;
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Management.Automation.Runspaces;
-using System.Management.Automation;
-using System.Management.Automation.Remoting;
-using System.Net;
-using System.Security.Cryptography.X509Certificates;
-using System.Text;
-using Microsoft.Extensions.Logging;
-using Keyfactor.Orchestrators.Extensions.Interfaces;
-using System.Linq;
-using System.IO;
-using Microsoft.PowerShell;
-
-namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore
-{
- internal class ClientPSCertStoreReEnrollment
- {
- private readonly ILogger _logger;
- private readonly IPAMSecretResolver _resolver;
-
- public ClientPSCertStoreReEnrollment(ILogger logger, IPAMSecretResolver resolver)
- {
- _logger = logger;
- _resolver = resolver;
- }
-
- public JobResult PerformReEnrollment(ReenrollmentJobConfiguration config, SubmitReenrollmentCSR submitReenrollment, CertStoreBindingTypeENUM bindingType)
- {
- bool hasError = false;
-
- try
- {
- _logger.MethodEntry();
- var serverUserName = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server UserName", config.ServerUsername);
- var serverPassword = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server Password", config.ServerPassword);
-
- // Extract values necessary to create remote PS connection
- JobProperties jobProperties = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties,
+// Copyright 2022 Keyfactor
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Ignore Spelling: Keyfactor
+
+// 021225 rcp Cleaned up and removed unnecessary code
+
+using Keyfactor.Logging;
+using Keyfactor.Orchestrators.Common.Enums;
+using Keyfactor.Orchestrators.Extensions;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Management.Automation;
+using System.Security.Cryptography.X509Certificates;
+using Microsoft.Extensions.Logging;
+using Keyfactor.Orchestrators.Extensions.Interfaces;
+using System.Linq;
+using Keyfactor.Extensions.Orchestrator.WindowsCertStore.IISU;
+using Keyfactor.Extensions.Orchestrator.WindowsCertStore.WinSql;
+
+namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore
+{
+ internal class ClientPSCertStoreReEnrollment
+ {
+ private readonly ILogger _logger;
+ private readonly IPAMSecretResolver _resolver;
+
+ private PSHelper _psHelper;
+ private Collection? _results;
+
+ public ClientPSCertStoreReEnrollment(ILogger logger, IPAMSecretResolver resolver)
+ {
+ _logger = logger;
+ _resolver = resolver;
+ }
+
+ public JobResult PerformReEnrollment(ReenrollmentJobConfiguration config, SubmitReenrollmentCSR submitReenrollment, CertStoreBindingTypeENUM bindingType)
+ {
+ JobResult jobResult = null;
+
+ try
+ {
+ _logger.MethodEntry();
+
+ var serverUserName = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server UserName", config.ServerUsername);
+ var serverPassword = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server Password", config.ServerPassword);
+
+ // Get JobProperties from Config
+ var subjectText = config.JobProperties["subjectText"] as string;
+ var providerName = config.JobProperties["ProviderName"] as string;
+ var keyType = config.JobProperties["keyType"] as string;
+ var SAN = config.JobProperties["SAN"] as string;
+
+ int keySize = 0;
+ if (config.JobProperties["keySize"] is not null && int.TryParse(config.JobProperties["keySize"].ToString(), out int size))
+ {
+ keySize = size;
+ }
+
+ // Extract values necessary to create remote PS connection
+ JobProperties jobProperties = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties,
new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
string protocol = jobProperties.WinRmProtocol;
string port = jobProperties.WinRmPort;
- bool IncludePortInSPN = jobProperties.SpnPortFlag;
+ bool includePortInSPN = jobProperties.SpnPortFlag;
string clientMachineName = config.CertificateStoreDetails.ClientMachine;
string storePath = config.CertificateStoreDetails.StorePath;
- _logger.LogTrace($"Establishing runspace on client machine: {clientMachineName}");
- using var runSpace = PsHelper.GetClientPsRunspace(protocol, clientMachineName, port, IncludePortInSPN, serverUserName, serverPassword);
-
- _logger.LogTrace("Runspace created");
- runSpace.Open();
- _logger.LogTrace("Runspace opened");
-
- PowerShell ps = PowerShell.Create();
- ps.Runspace = runSpace;
-
- string CSR = string.Empty;
-
- var subjectText = config.JobProperties["subjectText"];
- var providerName = config.JobProperties["ProviderName"];
- var keyType = config.JobProperties["keyType"];
- var keySize = config.JobProperties["keySize"];
- var SAN = config.JobProperties["SAN"];
-
- Collection results;
-
- // If the provider name is null, default it to the Microsoft CA
- providerName ??= "Microsoft Strong Cryptographic Provider";
-
- // Create the script file
- ps.AddScript("$infFilename = New-TemporaryFile");
- ps.AddScript("$csrFilename = New-TemporaryFile");
-
- ps.AddScript("if (Test-Path $csrFilename) { Remove-Item $csrFilename }");
-
- ps.AddScript($"Set-Content $infFilename -Value [NewRequest]");
- ps.AddScript($"Add-Content $infFilename -Value 'Subject = \"{subjectText}\"'");
- ps.AddScript($"Add-Content $infFilename -Value 'ProviderName = \"{providerName}\"'");
- ps.AddScript($"Add-Content $infFilename -Value 'MachineKeySet = True'");
- ps.AddScript($"Add-Content $infFilename -Value 'HashAlgorithm = SHA256'");
- ps.AddScript($"Add-Content $infFilename -Value 'KeyAlgorithm = {keyType}'");
- ps.AddScript($"Add-Content $infFilename -Value 'KeyLength={keySize}'");
- ps.AddScript($"Add-Content $infFilename -Value 'KeySpec = 0'");
-
- if (SAN != null)
- {
- ps.AddScript($"Add-Content $infFilename -Value '[Extensions]'");
- ps.AddScript(@"Add-Content $infFilename -Value '2.5.29.17 = ""{text}""'");
-
- foreach (string s in SAN.ToString().Split("&"))
- {
- ps.AddScript($"Add-Content $infFilename -Value '_continue_ = \"{s + "&"}\"'");
- }
- }
-
- try
- {
- // Get INF file for debugging
- ps.AddScript("$name = $infFilename.FullName");
- ps.AddScript("$name");
- results = ps.Invoke();
-
- string fname = results[0].ToString();
- string infContent = File.ReadAllText(fname);
-
- _logger.LogDebug($"Contents of {fname}:");
- _logger.LogDebug(infContent);
- }
- catch (Exception)
- {
- }
-
- // Execute the -new command
- ps.AddScript($"certreq -new -q $infFilename $csrFilename");
- _logger.LogDebug($"Subject Text: {subjectText}");
- _logger.LogDebug($"SAN: {SAN}");
- _logger.LogDebug($"Provider Name: {providerName}");
- _logger.LogDebug($"Key Type: {keyType}");
- _logger.LogDebug($"Key Size: {keySize}");
- _logger.LogTrace("Attempting to create the CSR by Invoking the script.");
-
- results = ps.Invoke();
- _logger.LogTrace("Completed the attempt in creating the CSR.");
-
- ps.Commands.Clear();
-
- try
- {
- ps.AddScript($"$CSR = Get-Content $csrFilename -Raw");
- _logger.LogTrace("Attempting to get the contents of the CSR file.");
- results = ps.Invoke();
- _logger.LogTrace("Finished getting the CSR Contents.");
- }
- catch (Exception)
- {
- var psError = ps.Streams.Error.ReadAll().Aggregate(String.Empty, (current, error) => current + error.ErrorDetails.Message);
-
- hasError = true;
-
- throw new CertificateStoreException($"Error creating CSR File. {psError}");
- }
- finally
- {
- ps.Commands.Clear();
-
- // Delete the temp files
- ps.AddScript("if (Test-Path $infFilename) { Remove-Item -Path $infFilename }");
- ps.AddScript("if (Test-Path $csrFilename) { Remove-Item -Path $csrFilename }");
- _logger.LogTrace("Attempt to delete the temporary files.");
- results = ps.Invoke();
-
- if (hasError) runSpace.Close();
+ //_psHelper = new(protocol, port, includePortInSPN, clientMachineName, serverUserName, serverPassword);
+
+ _psHelper = new(protocol, port, includePortInSPN, clientMachineName, serverUserName, serverPassword);
+ _psHelper.Initialize();
+
+ using (_psHelper)
+ {
+ // First create and return the CSR
+ _logger.LogTrace($"Subject Text: {subjectText}");
+ _logger.LogTrace($"Provider Name: {providerName}");
+ _logger.LogTrace($"Key Type: {keyType}");
+ _logger.LogTrace($"Key Size: {keySize}");
+ _logger.LogTrace($"SAN: {SAN}");
+
+ string csr = string.Empty;
+
+ try
+ {
+ _logger.LogTrace("Attempting to Create CSR");
+ csr = CreateCSR(subjectText, providerName, keyType, keySize, SAN);
+ _logger.LogTrace("Returned from creating CSR");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError($"Error while attempting to create the CSR: {ex.Message}");
+ throw new Exception($"Unable to create the CSR file. {ex.Message}");
+ }
+
+ _logger.LogTrace($"CSR Contents: '{csr}'");
+
+ if (csr != string.Empty)
+ {
+ // Submit and Sign the CSR in Command
+ _logger.LogTrace("Attempting to sign CSR");
+ X509Certificate2 myCert = submitReenrollment.Invoke(csr);
+
+ if (myCert == null) { throw new Exception("Command was unable to sign the CSR."); }
+
+ // Import the certificate
+ string thumbprint = ImportCertificate(myCert.RawData, storePath);
+
+ // If there is binding, bind it to the correct store type
+ if (thumbprint != null)
+ {
+ switch (bindingType)
+ {
+ case CertStoreBindingTypeENUM.WinIIS:
+ // Bind Certificate to IIS Site
+ IISBindingInfo bindingInfo = new IISBindingInfo(config.JobProperties);
+ WinIISBinding.BindCertificate(_psHelper, bindingInfo, thumbprint, "", storePath);
+ break;
+ case CertStoreBindingTypeENUM.WinSQL:
+ // Bind Certificate to SQL Instance
+ string sqlInstanceNames = "MSSQLSERVER";
+ if (config.JobProperties.ContainsKey("InstanceName"))
+ {
+ sqlInstanceNames = config.JobProperties["InstanceName"]?.ToString() ?? "MSSQLSERVER";
+ }
+ WinSqlBinding.BindSQLCertificate(_psHelper, sqlInstanceNames, thumbprint, "", storePath, false);
+ break;
+ }
+
+ }
+
+ jobResult = new JobResult
+ {
+ Result = OrchestratorJobStatusJobResult.Success,
+ JobHistoryId = config.JobHistoryId,
+ FailureMessage = ""
+ };
+
+ }
+ else
+ {
+ jobResult = new JobResult
+ {
+ Result = OrchestratorJobStatusJobResult.Failure,
+ JobHistoryId = config.JobHistoryId,
+ FailureMessage = "No CSR was generated to perform a reenrollment. Please check the logs for further details."
+ };
+
+ }
}
- // Get the byte array
- var RawContent = runSpace.SessionStateProxy.GetVariable("CSR");
-
- // Sign CSR in Keyfactor
- _logger.LogTrace("Get the signed CSR from KF.");
- X509Certificate2 myCert = submitReenrollment.Invoke(RawContent.ToString());
-
- if (myCert != null)
- {
- // Get the cert data into string format
- string csrData = Convert.ToBase64String(myCert.RawData, Base64FormattingOptions.InsertLineBreaks);
-
- _logger.LogTrace("Creating the text version of the certificate.");
-
- // Write out the cert file
- StringBuilder sb = new StringBuilder();
- sb.AppendLine("-----BEGIN CERTIFICATE-----");
- sb.AppendLine(csrData);
- sb.AppendLine("-----END CERTIFICATE-----");
-
- ps.AddScript("$cerFilename = New-TemporaryFile");
- ps.AddScript($"Set-Content $cerFilename '{sb}'");
-
- results = ps.Invoke();
- ps.Commands.Clear();
-
- // Accept the signed cert
- _logger.LogTrace("Attempting to accept or bind the certificate to the HSM.");
-
- ps.AddScript($"Set-Location -Path Cert:\\localmachine\\'{config.CertificateStoreDetails.StorePath}'");
- ps.AddScript($"Import-Certificate -Filepath $cerFilename");
- ps.Invoke();
- _logger.LogTrace("Successfully bound the certificate.");
-
- ps.Commands.Clear();
-
- // Delete the temp files
- ps.AddScript("if (Test-Path $infFilename) { Remove-Item -Path $infFilename }");
- ps.AddScript("if (Test-Path $csrFilename) { Remove-Item -Path $csrFilename }");
- ps.AddScript("if (Test-Path $cerFilename) { Remove-Item -Path $cerFilename }");
- _logger.LogTrace("Removing temporary files.");
- results = ps.Invoke();
-
- ps.Commands.Clear();
- runSpace.Close();
-
- // Default results
- JobResult result = new JobResult
+ return jobResult;
+
+ }
+ catch (Exception ex)
+ {
+ return new JobResult
+ {
+ Result = OrchestratorJobStatusJobResult.Failure,
+ JobHistoryId = config.JobHistoryId,
+ FailureMessage = ex.Message
+ };
+ }
+ finally
+ {
+ if (_psHelper != null)
+ {
+ _psHelper.Terminate();
+ }
+ }
+ }
+
+ private string CreateCSR(string subjectText, string providerName, string keyType, int keySize, string SAN)
+ {
+ string errorMsg = "";
+
+ try
+ {
+ string myCSR = "";
+
+ _logger.LogTrace("Entering ReEnrollment function: CreateCSR");
+
+ // Set the parameters for the function
+ var parameters = new Dictionary
+ {
+ { "subjectText", subjectText },
+ { "providerName", providerName },
+ { "keyType", keyType },
+ { "keyLength", keySize },
+ { "SAN", SAN }
+ };
+ _logger.LogInformation("Attempting to execute PS function (New-CsrEnrollment)");
+ _results = _psHelper.ExecutePowerShell("New-CsrEnrollment", parameters);
+ _logger.LogInformation("Returned from executing PS function (New-CsrEnrollment)");
+
+ // This should return the CSR that was generated
+ if (_results == null || _results.Count == 0)
+ {
+ _logger.LogError("No results were returned, resulting in no CSR created.");
+ }
+ else if (_results.Count == 1)
+ {
+ myCSR = _results[0]?.ToString();
+ if (!string.IsNullOrEmpty(myCSR))
{
- Result = OrchestratorJobStatusJobResult.Success,
- JobHistoryId = config.JobHistoryId,
- FailureMessage = ""
- };
-
- // Do specific bindings
- switch (bindingType)
+ _logger.LogTrace("Created a CSR.");
+ }
+ else
{
- case CertStoreBindingTypeENUM.WinIIS:
- // Bind the certificate to IIS
- ClientPSIIManager iisManager = new ClientPSIIManager(config, serverUserName, serverPassword);
- result = iisManager.BindCertificate(myCert);
- // Provide logging information
- if (result.Result == OrchestratorJobStatusJobResult.Success) { _logger.LogInformation("Certificate was successfully bound to the IIS Server."); }
- else { _logger.LogInformation("There was an issue while attempting to bind the certificate to the IIS Server. Check the logs for more information."); }
- break;
-
- case CertStoreBindingTypeENUM.WinSQL:
-
- // Bind to SQL Server
- ClientPsSqlManager sqlManager = new ClientPsSqlManager(config, serverUserName, serverPassword);
- result = sqlManager.BindCertificates("", myCert);
-
- // Provide logging information
- if (result.Result == OrchestratorJobStatusJobResult.Success) { _logger.LogInformation("Certificate was successfully bound to the SQL Server."); }
- else { _logger.LogInformation("There was an issue while attempting to bind the certificate to the SQL Server. Check the logs for more information."); }
- break;
-
- }
-
- ps.Commands.Clear();
- runSpace.Close();
-
- return result;
- }
- else
- {
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = config.JobHistoryId,
- FailureMessage = "The ReEnrollment job was unable to sign the CSR. Please check the formatting of the SAN and other ReEnrollment properties."
- };
- }
-
- }
- catch (PSRemotingTransportException psEx)
+ _logger.LogError("The returned result is empty, resulting in no CSR created.");
+ }
+ }
+ else // _results.Count > 1
+ {
+ var messages = string.Join(Environment.NewLine, _results.Select(r => r?.ToString()));
+ errorMsg = "Multiple results returned, indicating potential errors and no CSR was created.\n";
+ errorMsg += $"Details:{Environment.NewLine}{messages}";
+ _logger.LogError(errorMsg);
+
+ throw new ApplicationException(errorMsg);
+
+ }
+
+ return myCSR;
+ }
+ catch (ApplicationException appEx)
{
- var failureMessage = $"ReEnrollment job failed for Site '{config.CertificateStoreDetails.StorePath}' on server '{config.CertificateStoreDetails.ClientMachine}' with a PowerShell Transport Exception: {psEx.Message}";
- _logger.LogError(failureMessage + LogHandler.FlattenException(psEx));
-
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = config.JobHistoryId,
- FailureMessage = failureMessage
- };
-
- }
- catch (Exception ex)
- {
- var failureMessage = $"ReEnrollment job failed for Site '{config.CertificateStoreDetails.StorePath}' on server '{config.CertificateStoreDetails.ClientMachine}' with error: '{LogHandler.FlattenException(ex)}'";
- _logger.LogWarning(failureMessage);
-
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = config.JobHistoryId,
- FailureMessage = failureMessage
- };
- }
- }
- }
-}
+ throw new Exception(appEx.Message);
+ }
+ catch (Exception ex)
+ {
+ var failureMessage = $"ReEnrollment error at Creating CSR with error: '{ex.Message}'";
+ _logger.LogError(LogHandler.FlattenException(ex));
+
+ throw new Exception(failureMessage);
+ }
+ }
+
+ private string ImportCertificate(byte[] certificateRawData, string storeName)
+ {
+ try
+ {
+ string myThumbprint = "";
+
+ _logger.LogTrace("Entering ReEnrollment function: ImportCertificate");
+
+ // Set the parameters for the function
+ var parameters = new Dictionary
+ {
+ { "rawData", certificateRawData },
+ { "storeName", storeName }
+ };
+
+ _logger.LogTrace("Attempting to execute PS function (Import-SignedCertificate)");
+ _results = _psHelper.ExecutePowerShell("Import-SignedCertificate", parameters);
+ _logger.LogTrace("Returned from executing PS function (Import-SignedCertificate)");
+
+ // This should return the CSR that was generated
+ if (_results != null && _results.Count > 0)
+ {
+ myThumbprint = _results[0].ToString();
+ _logger.LogTrace($"Imported the CSR and returned the following thumbprint: {myThumbprint}");
+ }
+ else
+ {
+ _logger.LogError("No results were returned, resulting in no CSR created.");
+ }
+
+ return myThumbprint;
+
+ }
+ catch (Exception ex)
+ {
+ var failureMessage = $"ReEnrollment error while attempting to import the certificate with error: '{LogHandler.FlattenException(ex)}'";
+ _logger.LogError(failureMessage);
+
+ throw new Exception(failureMessage);
+ }
+ }
+
+ }
+}
diff --git a/IISU/ClientPSIIManager.cs b/IISU/ClientPSIIManager.cs
deleted file mode 100644
index 996b46d..0000000
--- a/IISU/ClientPSIIManager.cs
+++ /dev/null
@@ -1,687 +0,0 @@
-// Copyright 2022 Keyfactor
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-using Keyfactor.Extensions.Orchestrator.WindowsCertStore.Scripts;
-using Keyfactor.Logging;
-using Keyfactor.Orchestrators.Common.Enums;
-using Keyfactor.Orchestrators.Extensions;
-using Microsoft.Extensions.Logging;
-using Newtonsoft.Json;
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Linq;
-using System.Management.Automation;
-using System.Management.Automation.Runspaces;
-using System.Security.Cryptography.X509Certificates;
-
-namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore
-{
- public class ClientPSIIManager
- {
- private string SiteName { get; set; }
- private string Port { get; set; }
- private string Protocol { get; set; }
- private string HostName { get; set; }
- private string SniFlag { get; set; }
- private string IPAddress { get; set; }
-
- private string RenewalThumbprint { get; set; } = "";
-
- private string CertContents { get; set; } = "";
-
- private string PrivateKeyPassword { get; set; } = "";
-
- private string ClientMachineName { get; set; }
- private string StorePath { get; set; }
-
- private long JobHistoryID { get; set; }
-
- private readonly ILogger _logger;
- private readonly Runspace _runSpace;
-
- private PowerShell ps;
-
- ///
- /// This constructor is used for unit testing
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public ClientPSIIManager(Runspace runSpace, string SiteName, string Protocol, string IPAddress, string Port, string HostName, string Thumbprint, string StorePath, string sniFlag)
- {
- _logger = LogHandler.GetClassLogger();
- _runSpace = runSpace;
-
- this.SiteName = SiteName;
- this.Protocol = Protocol;
- this.IPAddress = IPAddress;
- this.Port = Port;
- this.HostName = HostName;
- this.RenewalThumbprint = Thumbprint;
- this.StorePath = StorePath;
- this.SniFlag = sniFlag;
- }
-
- public ClientPSIIManager(ReenrollmentJobConfiguration config, string serverUsername, string serverPassword)
- {
- _logger = LogHandler.GetClassLogger();
-
- try
- {
- SiteName = config.JobProperties["SiteName"].ToString();
- Port = config.JobProperties["Port"].ToString();
- HostName = config.JobProperties["HostName"]?.ToString();
- Protocol = config.JobProperties["Protocol"].ToString();
- SniFlag = MigrateSNIFlag(config.JobProperties["SniFlag"]?.ToString());
- IPAddress = config.JobProperties["IPAddress"].ToString();
-
- PrivateKeyPassword = ""; // A reenrollment does not have a PFX Password
- RenewalThumbprint = ""; // A reenrollment will always be empty
- CertContents = ""; // Not needed for a reenrollment
-
- ClientMachineName = config.CertificateStoreDetails.ClientMachine;
- StorePath = config.CertificateStoreDetails.StorePath;
-
- JobHistoryID = config.JobHistoryId;
-
- // Establish PowerShell Runspace
- var jobProperties = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
- string winRmProtocol = jobProperties.WinRmProtocol;
- string winRmPort = jobProperties.WinRmPort;
- bool includePortInSPN = jobProperties.SpnPortFlag;
-
- _logger.LogTrace($"Establishing runspace on client machine: {ClientMachineName}");
- _runSpace = PsHelper.GetClientPsRunspace(winRmProtocol, ClientMachineName, winRmPort, includePortInSPN, serverUsername, serverPassword);
- }
- catch (Exception e)
- {
- throw new Exception($"Error when initiating an IIS ReEnrollment Job: {e.Message}", e.InnerException);
- }
- }
-
- public ClientPSIIManager(ManagementJobConfiguration config, string serverUsername, string serverPassword)
- {
- _logger = LogHandler.GetClassLogger();
-
- try
- {
- _logger.LogTrace("Setting Job Properties");
- SiteName = config.JobProperties["SiteName"].ToString();
- Port = config.JobProperties["Port"].ToString();
- HostName = config.JobProperties["HostName"]?.ToString();
- Protocol = config.JobProperties["Protocol"].ToString();
- SniFlag = MigrateSNIFlag(config.JobProperties["SniFlag"]?.ToString());
- IPAddress = config.JobProperties["IPAddress"].ToString();
-
- PrivateKeyPassword = ""; // A reenrollment does not have a PFX Password
- RenewalThumbprint = ""; // This property will not be used for renewals starting in version 2.5
- CertContents = ""; // Not needed for a reenrollment
-
- _logger.LogTrace("Certificate details");
- ClientMachineName = config.CertificateStoreDetails.ClientMachine;
- StorePath = config.CertificateStoreDetails.StorePath;
-
- JobHistoryID = config.JobHistoryId;
-
- if (config.JobProperties.ContainsKey("RenewalThumbprint"))
- {
- RenewalThumbprint = config.JobProperties["RenewalThumbprint"].ToString();
- _logger.LogTrace($"Found Thumbprint Will Renew all Certs with this thumbprint: {RenewalThumbprint}");
- }
- else
- {
- _logger.LogTrace("No renewal Thumbprint was provided.");
- }
-
- // Establish PowerShell Runspace
- var jobProperties = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
- _logger.LogTrace($"Job properties value: {jobProperties}");
-
- string winRmProtocol = jobProperties.WinRmProtocol;
- string winRmPort = jobProperties.WinRmPort;
- bool includePortInSPN = jobProperties.SpnPortFlag;
-
- _logger.LogTrace($"Establishing runspace on client machine: {ClientMachineName}");
- _runSpace = PsHelper.GetClientPsRunspace(winRmProtocol, ClientMachineName, winRmPort, includePortInSPN, serverUsername, serverPassword);
- }
- catch (Exception e)
- {
- throw new Exception($"Error when initiating an IIS Management Job: {e.Message}", e.InnerException);
- }
- }
-
- public JobResult BindCertificate(X509Certificate2 x509Cert)
- {
- try
- {
- _logger.MethodEntry();
-
- _runSpace.Open();
- ps = PowerShell.Create();
- ps.Runspace = _runSpace;
-
- bool hadError = false;
- string errorMessage = string.Empty;
-
- try
- {
- Collection results = (Collection)PerformIISBinding(SiteName, Protocol, IPAddress, Port, HostName, SniFlag, x509Cert.Thumbprint, StorePath);
-
- if (ps.HadErrors)
- {
- var psError = ps.Streams.Error.ReadAll()
- .Aggregate(string.Empty, (current, error) =>
- current + (error.ErrorDetails != null && !string.IsNullOrEmpty(error.ErrorDetails.Message)
- ? error.ErrorDetails.Message
- : error.Exception != null
- ? error.Exception.Message
- : error.ToString()) + Environment.NewLine);
-
- errorMessage = psError;
- hadError = true;
-
- }
- }
- catch (Exception e)
- {
- string computerName = string.Empty;
- if (_runSpace.ConnectionInfo is null)
- {
- computerName = "localMachine";
- }
- else { computerName = "Server: " + _runSpace.ConnectionInfo.ComputerName; }
-
- errorMessage = $"Binding attempt failed on Site {SiteName} on {computerName}, Application error: {e.Message}";
- hadError = true;
- _logger.LogTrace(errorMessage);
- }
-
- if (hadError)
- {
- string computerName = string.Empty;
- if (_runSpace.ConnectionInfo is null)
- {
- computerName = "localMachine";
- }
- else { computerName = "Server: " + _runSpace.ConnectionInfo.ComputerName; }
-
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = JobHistoryID,
- FailureMessage = $"Binding attempt failed on Site {SiteName} on {computerName}: {errorMessage}"
- };
- }
- else
- {
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Success,
- JobHistoryId = JobHistoryID,
- FailureMessage = ""
- };
- }
- }
- catch (Exception e)
- {
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = JobHistoryID,
- FailureMessage = $"Application Error Occurred in BindCertificate: {LogHandler.FlattenException(e)}"
- };
- }
- finally
- {
- _runSpace.Close();
- ps.Runspace.Close();
- ps.Dispose();
- }
- }
-
- public JobResult UnBindCertificate()
- {
-#if NET8_0_OR_GREATER
- bool hadError = false;
- string errorMessage = string.Empty;
-
- try
- {
- _logger.MethodEntry();
-
- _runSpace.Open();
- ps = PowerShell.Create();
- ps.Runspace = _runSpace;
-
- Collection results = (Collection)PerformIISUnBinding(SiteName, Protocol, IPAddress, Port, HostName);
-
- if (ps.HadErrors)
- {
- var psError = ps.Streams.Error.ReadAll()
- .Aggregate(string.Empty, (current, error) =>
- current + (error.ErrorDetails != null && !string.IsNullOrEmpty(error.ErrorDetails.Message)
- ? error.ErrorDetails.Message
- : error.Exception != null
- ? error.Exception.Message
- : error.ToString()) + Environment.NewLine);
-
- errorMessage = psError;
- hadError = true;
-
- }
- }
- catch (Exception e)
- {
- string computerName = string.Empty;
- if (_runSpace.ConnectionInfo is null)
- {
- computerName = "localMachine";
- }
- else { computerName = "Server: " + _runSpace.ConnectionInfo.ComputerName; }
-
- errorMessage = $"Binding attempt failed on Site {SiteName} on {computerName}, Application error: {e.Message}";
- hadError = true;
- _logger.LogTrace(errorMessage);
- }
- finally
- {
- _runSpace.Close();
- ps.Runspace.Close();
- ps.Dispose();
- }
-
- if (hadError)
- {
- string computerName = string.Empty;
- if (_runSpace.ConnectionInfo is null)
- {
- computerName = "localMachine";
- }
- else { computerName = "Server: " + _runSpace.ConnectionInfo.ComputerName; }
-
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = JobHistoryID,
- FailureMessage = $"Binding attempt failed on Site {SiteName} on {computerName}: {errorMessage}"
- };
- }
- else
- {
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Success,
- JobHistoryId = JobHistoryID,
- FailureMessage = ""
- };
- }
-#endif
- try
- {
- _logger.MethodEntry();
-
- _runSpace.Open();
- ps = PowerShell.Create();
- ps.Runspace = _runSpace;
-
- ps.AddCommand("Import-Module")
- .AddParameter("Name", "WebAdministration")
- .AddStatement();
-
- _logger.LogTrace("WebAdministration Imported");
-
- ps.AddCommand("Get-WebBinding")
- .AddParameter("Protocol", Protocol)
- .AddParameter("Name", SiteName)
- .AddParameter("Port", Port)
- .AddParameter("HostHeader", HostName)
- .AddParameter("IPAddress", IPAddress)
- .AddStatement();
-
- _logger.LogTrace("Get-WebBinding Set");
- var foundBindings = ps.Invoke();
- _logger.LogTrace("foundBindings Invoked");
-
- if (foundBindings.Count == 0)
- {
- _logger.LogTrace($"{foundBindings.Count} Bindings Found...");
-
- string computerName = string.Empty;
- if (_runSpace.ConnectionInfo is null)
- {
- computerName = "localMachine";
- }
- else { computerName = "Server: " + _runSpace.ConnectionInfo.ComputerName; }
-
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Success,
- JobHistoryId = JobHistoryID,
- FailureMessage =
- $"No bindings we found for Site {SiteName} on {computerName}."
- };
- }
-
- //Log Commands out for debugging purposes
- foreach (var cmd in ps.Commands.Commands)
- {
- _logger.LogTrace("Logging PowerShell Command");
- _logger.LogTrace(cmd.CommandText);
- }
-
-
- foreach (var binding in foundBindings)
- {
- ps.AddCommand("Import-Module")
- .AddParameter("Name", "WebAdministration")
- .AddStatement();
-
- _logger.LogTrace("Imported WebAdministration Module");
-
- ps.AddCommand("Remove-WebBinding")
- .AddParameter("Name", SiteName)
- .AddParameter("BindingInformation",
- $"{binding.Properties["bindingInformation"]?.Value}")
- .AddStatement();
-
- //Log Commands out for debugging purposes
- foreach (var cmd in ps.Commands.Commands)
- {
- _logger.LogTrace("Logging PowerShell Command");
- _logger.LogTrace(cmd.CommandText);
- }
-
- var _ = ps.Invoke();
- _logger.LogTrace("Invoked Remove-WebBinding");
-
- if (ps.HadErrors)
- {
- _logger.LogTrace("PowerShell Had Errors");
- var psError = ps.Streams.Error.ReadAll().Aggregate(String.Empty, (current, error) => current + error.ErrorDetails.Message);
- //var psError = ps.Streams.Error.ReadAll()
- // .Aggregate(string.Empty, (current, error) =>
- // current + (error.ErrorDetails != null && !string.IsNullOrEmpty(error.ErrorDetails.Message)
- // ? error.ErrorDetails.Message
- // : error.Exception != null
- // ? error.Exception.Message
- // : error.ToString()) + Environment.NewLine);
-
- string computerName = string.Empty;
- if (_runSpace.ConnectionInfo is null)
- {
- computerName = "localMachine";
- }
- else { computerName = "Server: " + _runSpace.ConnectionInfo.ComputerName; }
-
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = JobHistoryID,
- FailureMessage =
- $"Failed to remove {Protocol} binding for Site {SiteName} on {computerName} not found, error {psError}"
- };
- }
- ps.Commands.Clear();
- }
-
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Success,
- JobHistoryId = JobHistoryID,
- FailureMessage = ""
- };
-
- }
- catch (Exception ex)
- {
- string computerName = string.Empty;
- if (_runSpace.ConnectionInfo is null)
- {
- computerName = "localMachine";
- }
- else { computerName = "Server: " + _runSpace.ConnectionInfo.ComputerName; }
-
- var failureMessage = $"Unbinding for Site '{StorePath}' on {computerName} with error: {LogHandler.FlattenException(ex)}";
- _logger.LogWarning(failureMessage);
-
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = JobHistoryID,
- FailureMessage = failureMessage
- };
- }
- finally
- {
- _runSpace.Close();
- ps.Runspace.Close();
- ps.Dispose();
- }
- }
-
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- private object PerformIISUnBinding(string webSiteName, string protocol, string ipAddress, string port, string hostName)
- {
- string funcScript = @"
- param (
- [string]$SiteName, # Name of the site
- [string]$IPAddress, # IP Address of the binding
- [string]$Port, # Port number of the binding
- [string]$Hostname, # Hostname (optional)
- [string]$Protocol = ""https"" # Protocol (default to """"https"""")
- )
-
- # Set Execution Policy (optional, depending on your environment)
- Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
-
- # Check if the IISAdministration module is already loaded
- if (-not (Get-Module -Name IISAdministration)) {
- try {
- # Attempt to import the IISAdministration module
- Import-Module IISAdministration -ErrorAction Stop
- }
- catch {
- throw ""Failed to load the IISAdministration module. Ensure it is installed and available.""
- }
- }
-
- try {
- # Get the bindings for the specified site
- $bindings = Get-IISSiteBinding -Name $SiteName
-
- # Check if any bindings match the specified criteria
- $matchingBindings = $bindings | Where-Object {
- ($_.bindingInformation -eq ""${IPAddress}:${Port}:${Hostname}"") -and
- ($_.protocol -eq $Protocol)
- }
-
- if ($matchingBindings) {
- # Unbind the matching certificates
- foreach ($binding in $matchingBindings) {
- Write-Host """"Removing binding: $($binding.bindingInformation) with protocol: $($binding.protocol)""""
- Write-Host """"Binding information:
- Remove-IISSiteBinding -Name $SiteName -BindingInformation $binding.bindingInformation -Protocol $binding.protocol -confirm:$false
- }
- Write-Host """"Successfully removed the matching bindings from the site: $SiteName""""
- } else {
- Write-Host """"No matching bindings found for site: $SiteName""""
- }
- }
- catch {
- throw ""An error occurred while unbinding the certificate from site ${SiteName}: $_""
- }
- ";
-
- ps.AddScript(funcScript);
- ps.AddParameter("SiteName", webSiteName);
- ps.AddParameter("IPAddress", ipAddress);
- ps.AddParameter("Port", port);
- ps.AddParameter("Hostname", hostName);
- ps.AddParameter("Protocol", protocol);
-
- _logger.LogTrace("funcScript added...");
- var results = ps.Invoke();
- _logger.LogTrace("funcScript Invoked...");
-
- return results;
- }
-
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- private object PerformIISBinding(string webSiteName, string protocol, string ipAddress, string port, string hostName, string sslFlags, string thumbprint, string storeName)
- {
- //string funcScript = @"
- // param (
- // $SiteName, # The name of the IIS site
- // $IPAddress, # The IP Address for the binding
- // $Port, # The port number for the binding
- // $Hostname, # Hostname for the binding (if any)
- // $Protocol, # Protocol (e.g., HTTP, HTTPS)
- // $Thumbprint, # Certificate thumbprint for HTTPS bindings
- // $StoreName, # Certificate store location (e.g., ""My"" for personal certs)
- // $SslFlags # SSL flags (if any)
- // )
-
- // # Set Execution Policy (optional, depending on your environment)
- // Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
-
- // ## Check if the IISAdministration module is available
- // #$module = Get-Module -Name IISAdministration -ListAvailable
-
- // #if (-not $module) {
- // # throw ""The IISAdministration module is not installed on this system.""
- // #}
-
- // # Check if the IISAdministration module is already loaded
- // if (-not (Get-Module -Name IISAdministration)) {
- // try {
- // # Attempt to import the IISAdministration module
- // Import-Module IISAdministration -ErrorAction Stop
- // }
- // catch {
- // throw ""Failed to load the IISAdministration module. Ensure it is installed and available.""
- // }
- // }
-
- // # Retrieve the existing binding information
- // $myBinding = ""${IPAddress}:${Port}:${Hostname}""
- // Write-Host ""myBinding: "" $myBinding
-
- // $siteBindings = Get-IISSiteBinding -Name $SiteName
- // $existingBinding = $siteBindings | Where-Object { $_.bindingInformation -eq $myBinding -and $_.protocol -eq $Protocol }
-
- // Write-Host ""Binding:"" $existingBinding
-
- // if ($null -ne $existingBinding) {
- // # Remove the existing binding
- // Remove-IISSiteBinding -Name $SiteName -BindingInformation $existingBinding.BindingInformation -Protocol $existingBinding.Protocol -Confirm:$false
-
- // Write-Host ""Removed existing binding: $($existingBinding.BindingInformation)""
- // }
-
- // # Create the new binding with modified properties
- // $newBindingInfo = ""${IPAddress}:${Port}:${Hostname}""
-
- // try
- // {
- // New-IISSiteBinding -Name $SiteName `
- // -BindingInformation $newBindingInfo `
- // -Protocol $Protocol `
- // -CertificateThumbprint $Thumbprint `
- // -CertStoreLocation $StoreName `
- // -SslFlag $SslFlags
-
- // Write-Host ""New binding added: $newBindingInfo""
- // }
- // catch {
- // throw $_
- // }
- //";
-#if NET6_0
- string funcScript = PowerShellScripts.UpdateIISBindingsV6;
-#elif NET8_0_OR_GREATER
- string funcScript = PowerShellScripts.UpdateIISBindingsV8;
-#endif
-
- ps.AddScript(funcScript);
- ps.AddParameter("SiteName", webSiteName);
- ps.AddParameter("IPAddress", ipAddress);
- ps.AddParameter("Port", port);
- ps.AddParameter("Hostname", hostName);
- ps.AddParameter("Protocol", protocol);
- ps.AddParameter("Thumbprint", thumbprint);
- ps.AddParameter("StoreName", storeName);
- ps.AddParameter("SslFlags", sslFlags);
-
- _logger.LogTrace("funcScript added...");
- var results = ps.Invoke();
- _logger.LogTrace("funcScript Invoked...");
-
- return results;
- }
-
- public static string MigrateSNIFlag(string input)
- {
- // Check if the input is numeric, if so, just return it as an integer
- if (int.TryParse(input, out int numericValue))
- {
- return numericValue.ToString();
- }
-
- if (string.IsNullOrEmpty(input)) { throw new ArgumentNullException("SNI/SSL Flag", "The SNI or SSL Flag flag must not be empty or null."); }
-
- // Handle the string cases
- switch (input.ToLower())
- {
- case "0 - no sni":
- return "0";
- case "1 - sni enabled":
- return "1";
- case "2 - non sni binding":
- return "2";
- case "3 - sni binding":
- return "3";
- default:
- throw new ArgumentOutOfRangeException($"Received an invalid value '{input}' for sni/ssl Flag value");
- }
- }
- }
-}
-
diff --git a/IISU/ClientPsSqlManager.cs b/IISU/ClientPsSqlManager.cs
deleted file mode 100644
index de3ab68..0000000
--- a/IISU/ClientPsSqlManager.cs
+++ /dev/null
@@ -1,421 +0,0 @@
-// Copyright 2022 Keyfactor
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-using Keyfactor.Logging;
-using Keyfactor.Orchestrators.Common.Enums;
-using Keyfactor.Orchestrators.Extensions;
-using Microsoft.Extensions.Logging;
-using Microsoft.Management.Infrastructure.Serialization;
-using Newtonsoft.Json;
-using System;
-using System.Linq;
-using System.Management.Automation;
-using System.Management.Automation.Runspaces;
-using System.Net;
-using System.Security.Cryptography.X509Certificates;
-using System.Web.Services.Description;
-
-namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore
-{
- internal class ClientPsSqlManager
- {
- private string SqlServiceUser { get; set; }
- private string SqlInstanceName { get; set; }
- private bool RestartService { get; set; }
- private string RegistryPath { get; set; }
- private string RenewalThumbprint { get; set; } = "";
- private string ClientMachineName { get; set; }
- private long JobHistoryID { get; set; }
-
- private readonly ILogger _logger;
- private readonly Runspace _runSpace;
-
- private PowerShell ps;
-
- public ClientPsSqlManager(ManagementJobConfiguration config, string serverUsername, string serverPassword)
- {
- _logger = LogHandler.GetClassLogger();
-
- try
- {
- ClientMachineName = config.CertificateStoreDetails.ClientMachine;
- JobHistoryID = config.JobHistoryId;
-
- if (config.JobProperties.ContainsKey("InstanceName"))
- {
- var instanceRef = config.JobProperties["InstanceName"]?.ToString();
- SqlInstanceName = string.IsNullOrEmpty(instanceRef) ? "MSSQLSERVER":instanceRef;
- }
-
- // Establish PowerShell Runspace
- var jobProperties = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
- string winRmProtocol = jobProperties.WinRmProtocol;
- string winRmPort = jobProperties.WinRmPort;
- bool includePortInSPN = jobProperties.SpnPortFlag;
- RestartService = jobProperties.RestartService;
-
- _logger.LogTrace($"Establishing runspace on client machine: {ClientMachineName}");
- _runSpace = PsHelper.GetClientPsRunspace(winRmProtocol, ClientMachineName, winRmPort, includePortInSPN, serverUsername, serverPassword);
- }
- catch (Exception e)
- {
- throw new Exception($"Error when initiating a SQL Management Job: {e.Message}", e.InnerException);
- }
- }
-
- public ClientPsSqlManager(InventoryJobConfiguration config,Runspace runSpace)
- {
- _logger = LogHandler.GetClassLogger();
-
- try
- {
- ClientMachineName = config.CertificateStoreDetails.ClientMachine;
- JobHistoryID = config.JobHistoryId;
-
- // Establish PowerShell Runspace
- var jobProperties = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
- string winRmProtocol = jobProperties.WinRmProtocol;
- string winRmPort = jobProperties.WinRmPort;
- bool includePortInSPN = jobProperties.SpnPortFlag;
-
- _logger.LogTrace($"Establishing runspace on client machine: {ClientMachineName}");
- _runSpace = runSpace;
- }
- catch (Exception e)
- {
- throw new Exception($"Error when initiating a SQL Inventory Job: {e.Message}", e.InnerException);
- }
- }
-
- public ClientPsSqlManager(ReenrollmentJobConfiguration config, string serverUsername, string serverPassword)
- {
- _logger = LogHandler.GetClassLogger();
-
- try
- {
- ClientMachineName = config.CertificateStoreDetails.ClientMachine;
- JobHistoryID = config.JobHistoryId;
-
- if (config.JobProperties.ContainsKey("InstanceName"))
- {
- var instanceRef = config.JobProperties["InstanceName"]?.ToString();
- SqlInstanceName = string.IsNullOrEmpty(instanceRef) ? "MSSQLSERVER" : instanceRef;
- }
-
- // Establish PowerShell Runspace
- var jobProperties = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
- string winRmProtocol = jobProperties.WinRmProtocol;
- string winRmPort = jobProperties.WinRmPort;
- bool includePortInSPN = jobProperties.SpnPortFlag;
- RestartService = jobProperties.RestartService;
-
- _logger.LogTrace($"Establishing runspace on client machine: {ClientMachineName}");
- _runSpace = PsHelper.GetClientPsRunspace(winRmProtocol, ClientMachineName, winRmPort, includePortInSPN, serverUsername, serverPassword);
- }
- catch (Exception e)
- {
- throw new Exception($"Error when initiating a SQL ReEnrollment Job: {e.Message}", e.InnerException);
- }
- }
-
- public JobResult UnBindCertificate()
- {
- try
- {
- _logger.MethodEntry();
-
- _runSpace.Open();
- ps = PowerShell.Create();
- ps.Runspace = _runSpace;
-
- RegistryPath = GetSqlCertRegistryLocation(SqlInstanceName, ps);
-
- var funcScript = string.Format($"Clear-ItemProperty -Path \"{RegistryPath}\" -Name Certificate");
- foreach (var cmd in ps.Commands.Commands)
- {
- _logger.LogTrace("Logging PowerShell Command");
- _logger.LogTrace(cmd.CommandText);
- }
-
- _logger.LogTrace($"funcScript {funcScript}");
- ps.AddScript(funcScript);
- _logger.LogTrace("funcScript added...");
- ps.Invoke();
- _logger.LogTrace("funcScript Invoked...");
-
- if (ps.HadErrors)
- {
- var psError = ps.Streams.Error.ReadAll()
- .Aggregate(string.Empty, (current, error) => current + error.ErrorDetails.Message);
- {
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = JobHistoryID,
- FailureMessage = $"Unable to unbind certificate to Sql Server"
- };
- }
- }
-
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Success,
- JobHistoryId = JobHistoryID,
- FailureMessage = ""
- };
- }
- catch (Exception e)
- {
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = JobHistoryID,
- FailureMessage = $"Error Occurred in unbind {LogHandler.FlattenException(e)}"
- };
- }
- finally
- {
- _runSpace.Close();
- ps.Runspace.Close();
- ps.Dispose();
- }
- }
-
- public string GetSqlInstanceValue(string instanceName,PowerShell ps)
- {
- try
- {
- var funcScript = string.Format(@$"Get-ItemPropertyValue ""HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL"" -Name {instanceName}");
- foreach (var cmd in ps.Commands.Commands)
- {
- _logger.LogTrace("Logging PowerShell Command");
- _logger.LogTrace(cmd.CommandText);
- }
-
- _logger.LogTrace($"funcScript {funcScript}");
- ps.AddScript(funcScript);
- _logger.LogTrace("funcScript added...");
- var SqlInstanceValue = ps.Invoke()[0].ToString();
- _logger.LogTrace("funcScript Invoked...");
- ps.Commands.Clear();
-
- if (!ps.HadErrors)
- {
- return SqlInstanceValue;
- }
- return null;
- }
- catch (ArgumentOutOfRangeException)
- {
- throw new Exception($"There were no SQL instances with the name: {instanceName}. Please check the spelling of the SQL instance.");
- }
- catch (Exception e)
- {
- throw new Exception($"Error when initiating getting instance name from registry: {e.Message}", e.InnerException);
- }
- }
-
- public string GetSqlCertRegistryLocation(string instanceName,PowerShell ps)
- {
- return $"HKLM:\\SOFTWARE\\Microsoft\\Microsoft SQL Server\\{GetSqlInstanceValue(instanceName,ps)}\\MSSQLServer\\SuperSocketNetLib\\";
- }
-
- public string GetSqlServerServiceName(string instanceName)
- {
- if(string.IsNullOrEmpty(instanceName))
- return string.Empty;
-
- //Default SQL Instance has this format
- if (instanceName == "MSSQLSERVER")
- return "MSSQLSERVER";
-
- //Named Instance service has this format
- return $"MSSQL`${instanceName}";
- }
-
- public JobResult BindCertificates(string renewalThumbprint, X509Certificate2 x509Cert)
- {
- try
- {
- var bindingError = string.Empty;
- RenewalThumbprint = renewalThumbprint;
-
- _runSpace.Open();
- ps = PowerShell.Create();
- ps.Runspace = _runSpace;
- if (!string.IsNullOrEmpty(renewalThumbprint))
- {
- var funcScript = string.Format(@$"(Get-ItemProperty ""HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server"").InstalledInstances");
- ps.AddScript(funcScript);
- _logger.LogTrace("funcScript added...");
- var instances = ps.Invoke();
- ps.Commands.Clear();
- foreach (var instance in instances)
- {
- var regLocation = GetSqlCertRegistryLocation(instance.ToString(), ps);
-
- funcScript = string.Format(@$"Get-ItemPropertyValue ""{regLocation}"" -Name Certificate");
- ps.AddScript(funcScript);
- _logger.LogTrace("funcScript added...");
- var thumbprint = ps.Invoke()[0].ToString();
- ps.Commands.Clear();
-
- if (RenewalThumbprint.Contains(thumbprint, StringComparison.CurrentCultureIgnoreCase))
- {
- bindingError=BindCertificate(x509Cert, ps);
- }
- }
- }
- else
- {
- bindingError=BindCertificate(x509Cert, ps);
- }
-
- if (bindingError.Length == 0)
- {
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Success,
- JobHistoryId = JobHistoryID,
- FailureMessage = ""
- };
- }
- else
- {
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = JobHistoryID,
- FailureMessage = bindingError
- };
- }
-
- }
- catch (Exception e)
- {
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = JobHistoryID,
- FailureMessage = $"Error Occurred in BindCertificates {LogHandler.FlattenException(e)}"
- };
- }
- finally
- {
- _runSpace.Close();
- ps.Runspace.Close();
- ps.Dispose();
- }
-
- }
- public string BindCertificate(X509Certificate2 x509Cert,PowerShell ps)
- {
- try
- {
- _logger.MethodEntry();
-
-
- //If they comma separated the instance entry param, they are trying to install to more than 1 instance
- var instances = SqlInstanceName.Split(',');
-
- foreach (var instanceName in instances)
- {
- RegistryPath = GetSqlCertRegistryLocation(instanceName, ps);
-
- var thumbPrint = string.Empty;
- if (x509Cert != null)
- thumbPrint = x509Cert.Thumbprint.ToLower(); //sql server config mgr expects lower
-
- var funcScript = string.Format($"Set-ItemProperty -Path \"{RegistryPath}\" -Name Certificate {thumbPrint}");
- foreach (var cmd in ps.Commands.Commands)
- {
- _logger.LogTrace("Logging PowerShell Command");
- _logger.LogTrace(cmd.CommandText);
- }
-
- ps.AddScript(funcScript);
- _logger.LogTrace($"Running script: {funcScript}");
- ps.Invoke();
- _logger.LogTrace("funcScript Invoked...");
-
- _logger.LogTrace("Setting up Acl Access for Manage Private Keys");
- ps.Commands.Clear();
-
- //Get the SqlServer Service User Name
- var serviceName = GetSqlServerServiceName(instanceName);
- if (serviceName != "")
- {
- _logger.LogTrace($"Service Name: {serviceName} was returned.");
-
- funcScript = @$"(Get-WmiObject Win32_Service -Filter ""Name='{serviceName}'"").StartName";
- ps.AddScript(funcScript);
- _logger.LogTrace($"Running script: {funcScript}");
- SqlServiceUser = ps.Invoke()[0].ToString();
-
- _logger.LogTrace($"SqlServiceUser: {SqlServiceUser}");
- _logger.LogTrace("Got service login user for ACL Permissions");
- ps.Commands.Clear();
-
- funcScript = $@"$thumbprint = '{thumbPrint}'
- $Cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object {{ $_.Thumbprint -eq $thumbprint }}
- $privKey = $Cert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName
- $keyPath = ""$($env:ProgramData)\Microsoft\Crypto\RSA\MachineKeys\""
- $privKeyPath = (Get-Item ""$keyPath\$privKey"")
- $Acl = Get-Acl $privKeyPath
- $Ar = New-Object System.Security.AccessControl.FileSystemAccessRule(""{SqlServiceUser.Replace("$", "`$")}"", ""Read"", ""Allow"")
- $Acl.SetAccessRule($Ar)
- Set-Acl $privKeyPath.FullName $Acl";
-
- ps.AddScript(funcScript);
- ps.Invoke();
- _logger.LogTrace("ACL FuncScript Invoked...");
-
- }
- else
- {
- _logger.LogTrace("No Service User has been returned. Skipping ACL update.");
- }
-
- //If user filled in a service name in the store then restart the SQL Server Services
- if (RestartService)
- {
- _logger.LogTrace("Starting to Restart SQL Server Service...");
- ps.Commands.Clear();
- funcScript = $@"Restart-Service -Name ""{serviceName}"" -Force";
-
- ps.AddScript(funcScript);
- ps.Invoke();
- _logger.LogTrace("Invoked Restart SQL Server Service....");
- }
-
- if (ps.HadErrors)
- {
- var psError = ps.Streams.Error.ReadAll()
- .Aggregate(string.Empty, (current, error) => current + error?.Exception.Message);
- {
- return psError;
- }
- }
- }
- return "";
- }
- catch (Exception e)
- {
- return LogHandler.FlattenException(e);
- }
-
- }
- }
-}
-
diff --git a/IISU/ImplementedStoreTypes/Win/Inventory.cs b/IISU/ImplementedStoreTypes/Win/Inventory.cs
index c04f322..9f0dd2a 100644
--- a/IISU/ImplementedStoreTypes/Win/Inventory.cs
+++ b/IISU/ImplementedStoreTypes/Win/Inventory.cs
@@ -12,12 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// 021225 rcp 2.6.0 Cleaned up and verified code
+
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Management.Automation;
-using System.Management.Automation.Runspaces;
-using System.Net;
-using System.Security.Cryptography.X509Certificates;
using Keyfactor.Logging;
using Keyfactor.Orchestrators.Common.Enums;
using Keyfactor.Orchestrators.Extensions;
@@ -31,6 +31,13 @@ public class Inventory : WinCertJobTypeBase, IInventoryJobExtension
{
private ILogger _logger;
public string ExtensionName => "WinCertInventory";
+
+ Collection? results = null;
+
+ public Inventory()
+ {
+
+ }
public Inventory(IPAMSecretResolver resolver)
{
@@ -42,53 +49,46 @@ public JobResult ProcessJob(InventoryJobConfiguration jobConfiguration, SubmitIn
_logger = LogHandler.GetClassLogger();
_logger.MethodEntry();
- return PerformInventory(jobConfiguration, submitInventoryUpdate);
- }
-
- private JobResult PerformInventory(InventoryJobConfiguration config, SubmitInventoryUpdate submitInventory)
- {
try
{
+
var inventoryItems = new List();
- _logger.LogTrace(JobConfigurationParser.ParseInventoryJobConfiguration(config));
+ _logger.LogTrace(JobConfigurationParser.ParseInventoryJobConfiguration(jobConfiguration));
- string serverUserName = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server UserName", config.ServerUsername);
- string serverPassword = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server Password", config.ServerPassword);
+ string serverUserName = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server UserName", jobConfiguration.ServerUsername);
+ string serverPassword = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server Password", jobConfiguration.ServerPassword);
// Deserialize specific job properties
- var jobProperties = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
+ var jobProperties = JsonConvert.DeserializeObject(jobConfiguration.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
string protocol = jobProperties.WinRmProtocol;
string port = jobProperties.WinRmPort;
bool IncludePortInSPN = jobProperties.SpnPortFlag;
- string clientMachineName = config.CertificateStoreDetails.ClientMachine;
- string storePath = config.CertificateStoreDetails.StorePath;
+ string clientMachineName = jobConfiguration.CertificateStoreDetails.ClientMachine;
+ string storePath = jobConfiguration.CertificateStoreDetails.StorePath;
if (storePath != null)
{
- _logger.LogTrace($"Establishing runspace on client machine: {clientMachineName}");
- using var myRunspace = PsHelper.GetClientPsRunspace(protocol, clientMachineName, port, IncludePortInSPN, serverUserName, serverPassword);
- myRunspace.Open();
-
- _logger.LogTrace("Runspace is now open");
- _logger.LogTrace($"Attempting to read certificates from cert store: {storePath}");
-
- //foreach (Certificate cert in PowerShellUtilities.CertificateStore.GetCertificatesFromStore(myRunspace, storePath))
- WinInventory winInv = new WinInventory(_logger);
- inventoryItems = winInv.GetInventoryItems(myRunspace, storePath);
-
- _logger.LogTrace($"A total of {inventoryItems.Count} were found");
- _logger.LogTrace("Closing runspace");
- myRunspace.Close();
-
- _logger.LogTrace("Invoking Inventory...");
- submitInventory.Invoke(inventoryItems);
- _logger.LogTrace($"Inventory Invoked ... {inventoryItems.Count} Items");
+ // Create the remote connection class to pass to Inventory Class
+ RemoteSettings settings = new();
+ settings.ClientMachineName = jobConfiguration.CertificateStoreDetails.ClientMachine;
+ settings.Protocol = jobProperties.WinRmProtocol;
+ settings.Port = jobProperties.WinRmPort;
+ settings.IncludePortInSPN = jobProperties.SpnPortFlag;
+ settings.ServerUserName = serverUserName;
+ settings.ServerPassword = serverPassword;
+
+ _logger.LogTrace($"Querying Window certificate in store: {storePath}");
+ inventoryItems = QueryWinCertCertificates(settings, storePath);
+
+ _logger.LogTrace("Invoking submitInventory..");
+ submitInventoryUpdate.Invoke(inventoryItems);
+ _logger.LogTrace($"submitInventory Invoked... {inventoryItems.Count} Items");
return new JobResult
{
Result = OrchestratorJobStatusJobResult.Success,
- JobHistoryId = config.JobHistoryId,
+ JobHistoryId = jobConfiguration.JobHistoryId,
FailureMessage = ""
};
}
@@ -96,36 +96,74 @@ private JobResult PerformInventory(InventoryJobConfiguration config, SubmitInven
return new JobResult
{
Result = OrchestratorJobStatusJobResult.Warning,
- JobHistoryId = config.JobHistoryId,
+ JobHistoryId = jobConfiguration.JobHistoryId,
FailureMessage =
$"No certificates were found in the Certificate Store Path: {storePath} on server: {clientMachineName}"
};
}
- catch (CertificateStoreException psEx)
- {
- _logger.LogTrace(psEx.Message);
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = config.JobHistoryId,
- FailureMessage =
- $"Unable to open remote certificate store: {LogHandler.FlattenException(psEx)}"
- };
- }
catch (Exception ex)
{
_logger.LogTrace(LogHandler.FlattenException(ex));
- var failureMessage = $"Inventory job failed for Site '{config.CertificateStoreDetails.StorePath}' on server '{config.CertificateStoreDetails.ClientMachine}' with error: '{LogHandler.FlattenException(ex)}'";
+ var failureMessage = $"Inventory job failed for Site '{jobConfiguration.CertificateStoreDetails.StorePath}' on server '{jobConfiguration.CertificateStoreDetails.ClientMachine}' with error: '{LogHandler.FlattenException(ex)}'";
_logger.LogWarning(failureMessage);
return new JobResult
{
Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = config.JobHistoryId,
+ JobHistoryId = jobConfiguration.JobHistoryId,
FailureMessage = failureMessage
};
}
+
+ }
+
+ public List QueryWinCertCertificates(RemoteSettings settings, string StoreName)
+ {
+ List Inventory = new();
+
+ using (PSHelper ps = new(settings.Protocol, settings.Port, settings.IncludePortInSPN, settings.ClientMachineName, settings.ServerUserName, settings.ServerPassword))
+ {
+ ps.Initialize();
+
+ var parameters = new Dictionary
+ {
+ { "StoreName", StoreName }
+ };
+
+ results = ps.ExecutePowerShell("Get-KFCertificates", parameters);
+
+ // If there are certificates, deserialize the results and send them back to command
+ if (results != null && results.Count > 0)
+ {
+ var jsonResults = results[0].ToString();
+ var certInfoList = Certificate.Utilities.DeserializeCertificates(jsonResults); // JsonConvert.DeserializeObject>(jsonResults);
+
+ foreach (WinCertCertificateInfo cert in certInfoList)
+ {
+ var siteSettingsDict = new Dictionary
+ {
+ { "ProviderName", cert.ProviderName},
+ { "SAN", cert.SAN }
+ };
+
+ Inventory.Add(
+ new CurrentInventoryItem
+ {
+ Certificates = new[] { cert.Base64Data },
+ Alias = cert.Thumbprint,
+ PrivateKeyEntry = cert.HasPrivateKey,
+ UseChainLevel = false,
+ ItemStatus = OrchestratorInventoryItemStatus.Unknown,
+ Parameters = siteSettingsDict
+ }
+ );
+ }
+ }
+ ps.Terminate();
+ }
+
+ return Inventory;
}
}
}
diff --git a/IISU/ImplementedStoreTypes/Win/Management.cs b/IISU/ImplementedStoreTypes/Win/Management.cs
index b897987..0a0be75 100644
--- a/IISU/ImplementedStoreTypes/Win/Management.cs
+++ b/IISU/ImplementedStoreTypes/Win/Management.cs
@@ -14,30 +14,34 @@
// Ignore Spelling: Keyfactor
+// 021225 rcp 2.6.0 Cleaned up and verified code
+
using Keyfactor.Orchestrators.Common.Enums;
using Keyfactor.Orchestrators.Extensions;
using Keyfactor.Orchestrators.Extensions.Interfaces;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
-using System.Collections.Generic;
-using System.Management.Automation.Runspaces;
using System.Management.Automation;
-using System.Net;
using Keyfactor.Logging;
-using System.IO;
+using System.Collections.ObjectModel;
+using System.Collections.Generic;
namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.WinCert
{
public class Management : WinCertJobTypeBase, IManagementJobExtension
{
- private ILogger _logger;
-
public string ExtensionName => "WinCertManagement";
+ private ILogger _logger;
- private Runspace myRunspace;
+ private PSHelper _psHelper;
+ private Collection? _results = null;
- private string _thumbprint = string.Empty;
+ // Function wide config values
+ private string _clientMachineName = string.Empty;
+ private string _storePath = string.Empty;
+ private long _jobHistoryID = 0;
+ private CertStoreOperationType _operationType;
public Management(IPAMSecretResolver resolver)
{
@@ -48,6 +52,7 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
{
try
{
+ // Do some setup stuff
_logger = LogHandler.GetClassLogger();
_logger.MethodEntry();
@@ -60,146 +65,172 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
_logger.LogTrace(e.Message);
}
- string serverUserName = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server UserName", config.ServerUsername);
- string serverPassword = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server Password", config.ServerPassword);
-
- var jobProperties = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
- string protocol = jobProperties.WinRmProtocol;
- string port = jobProperties.WinRmPort;
- bool IncludePortInSPN = jobProperties.SpnPortFlag;
- string clientMachineName = config.CertificateStoreDetails.ClientMachine;
- string storePath = config.CertificateStoreDetails.StorePath;
- long JobHistoryID = config.JobHistoryId;
-
- _logger.LogTrace($"Establishing runspace on client machine: {clientMachineName}");
- myRunspace = PsHelper.GetClientPsRunspace(protocol, clientMachineName, port, IncludePortInSPN, serverUserName, serverPassword);
-
var complete = new JobResult
{
Result = OrchestratorJobStatusJobResult.Failure,
JobHistoryId = config.JobHistoryId,
- FailureMessage =
- "Invalid Management Operation"
+ FailureMessage = "Invalid Management Operation"
};
- switch (config.OperationType)
+ // Start parsing config information and establishing PS Session
+ _jobHistoryID = config.JobHistoryId;
+ _storePath = config.CertificateStoreDetails.StorePath;
+ _clientMachineName = config.CertificateStoreDetails.ClientMachine;
+ _operationType = config.OperationType;
+
+ string serverUserName = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server UserName", config.ServerUsername);
+ string serverPassword = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server Password", config.ServerPassword);
+
+ var jobProperties = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
+
+ string protocol = jobProperties?.WinRmProtocol;
+ string port = jobProperties?.WinRmPort;
+ bool includePortInSPN = (bool)jobProperties?.SpnPortFlag;
+
+ _psHelper = new(protocol, port, includePortInSPN, _clientMachineName, serverUserName, serverPassword);
+
+ switch (_operationType)
{
case CertStoreOperationType.Add:
{
- myRunspace.Open();
- _logger.LogTrace("runSpace Opened");
-
- complete = performAddition(config);
+ string certificateContents = config.JobCertificate.Contents;
+ string privateKeyPassword = config.JobCertificate.PrivateKeyPassword;
+ string? cryptoProvider = config.JobProperties["ProviderName"]?.ToString();
- myRunspace.Close();
- _logger.LogTrace($"RunSpace was closed...");
+ complete = AddCertificate(certificateContents, privateKeyPassword, cryptoProvider);
+ _logger.LogTrace($"Completed adding the certificate to the store");
break;
}
case CertStoreOperationType.Remove:
{
- myRunspace.Open();
- _logger.LogTrace("runSpace Opened");
-
- complete = performRemove(config);
+ string thumbprint = config.JobCertificate.Alias;
- myRunspace.Close();
- _logger.LogTrace($"RunSpace was closed...");
+ complete = RemoveCertificate(thumbprint);
+ _logger.LogTrace($"Completed removing the certificate from the store");
break;
}
}
+ _logger.MethodExit();
return complete;
}
- catch (Exception e)
+ catch (Exception ex)
{
- _logger.LogError($"Error Occurred in Management.PerformManagement: {e.Message}");
- throw;
+ _logger.LogTrace(LogHandler.FlattenException(ex));
+
+ var failureMessage = $"Management job {_operationType} failed on Store '{_storePath}' on server '{_clientMachineName}' with error: '{LogHandler.FlattenException(ex)}'";
+ _logger.LogWarning(failureMessage);
+
+ return new JobResult
+ {
+ Result = OrchestratorJobStatusJobResult.Failure,
+ JobHistoryId = _jobHistoryID,
+ FailureMessage = failureMessage
+ };
}
}
- private JobResult performAddition(ManagementJobConfiguration config)
+ public JobResult AddCertificate(string certificateContents, string privateKeyPassword, string cryptoProvider)
{
try
{
-#nullable enable
- string certificateContents = config.JobCertificate.Contents;
- string privateKeyPassword = config.JobCertificate.PrivateKeyPassword;
- string storePath = config.CertificateStoreDetails.StorePath;
- long jobNumber = config.JobHistoryId;
- string? cryptoProvider = config.JobProperties["ProviderName"]?.ToString();
-#nullable disable
-
- // If a crypto provider was provided, check to see if it exists
- if (cryptoProvider != null)
+ using (_psHelper)
{
- _logger.LogInformation($"Checking the server for the crypto provider: {cryptoProvider}");
- if (!PsHelper.IsCSPFound(PsHelper.GetCSPList(myRunspace), cryptoProvider))
- { throw new Exception($"The Crypto Provider: {cryptoProvider} was not found. Please check the spelling and accuracy of the Crypto Provider Name provided. If unsure which provider to use, leave the field blank and the default crypto provider will be used."); }
+ _psHelper.Initialize();
+
+ _logger.LogTrace("Attempting to execute PS function (Add-KFCertificateToStore)");
+
+ // Mandatory parameters
+ var parameters = new Dictionary
+ {
+ { "Base64Cert", certificateContents },
+ { "StoreName", _storePath },
+ };
+
+ // Optional parameters
+ if (!string.IsNullOrEmpty(privateKeyPassword)) { parameters.Add("PrivateKeyPassword", privateKeyPassword); }
+ if (!string.IsNullOrEmpty(cryptoProvider)) { parameters.Add("CryptoServiceProvider", cryptoProvider); }
+
+ _results = _psHelper.ExecutePowerShell("Add-KFCertificateToStore", parameters);
+ _logger.LogTrace("Returned from executing PS function (Add-KFCertificateToStore)");
+
+ // This should return the thumbprint of the certificate
+ if (_results != null && _results.Count > 0)
+ {
+ var thumbprint = _results[0].ToString();
+ _logger.LogTrace($"Added certificate to store {_storePath}, returned with the thumbprint {thumbprint}");
+ }
+ else
+ {
+ _logger.LogTrace("No results were returned. There could have been an error while adding the certificate. Look in the trace logs for PowerShell information.");
+ }
+ _psHelper.Terminate();
}
- if (storePath != null)
+ return new JobResult
{
- _logger.LogInformation($"Attempting to add WinCert certificate to cert store: {storePath}");
-
- ClientPSCertStoreManager manager = new ClientPSCertStoreManager(_logger, myRunspace, jobNumber);
-
- // Write the certificate contents to a temporary file on the remote computer, returning the filename.
- _logger.LogTrace($"Creating temporary pfx file.");
- string filePath = manager.CreatePFXFile(certificateContents, privateKeyPassword);
-
- // Using certutil on the remote computer, import the pfx file using a supplied csp if any.
- _logger.LogTrace($"Importing temporary PFX File: {filePath}.");
- JobResult result = manager.ImportPFXFile(filePath, privateKeyPassword, cryptoProvider, storePath);
-
- // Delete the temporary file
- _logger.LogTrace($"Deleting temporary PFX File: {filePath}.");
- manager.DeletePFXFile(Path.GetDirectoryName(filePath), Path.GetFileNameWithoutExtension(filePath));
+ Result = OrchestratorJobStatusJobResult.Success,
+ JobHistoryId = _jobHistoryID,
+ FailureMessage = ""
+ };
- return result;
- }
- else
- {
- throw new Exception($"The store path is empty or null.");
- }
}
- catch (Exception e)
+ catch (Exception ex)
{
+ var failureMessage = $"Management job {_operationType} failed on Store '{_storePath}' on server '{_clientMachineName}' with error: '{LogHandler.FlattenException(ex)}'";
+ _logger.LogWarning(failureMessage);
+
return new JobResult
{
Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = config.JobHistoryId,
- FailureMessage =
- $"Management/Add {e.Message}"
+ JobHistoryId = _jobHistoryID,
+ FailureMessage = failureMessage
};
}
}
- private JobResult performRemove(ManagementJobConfiguration config)
+ public JobResult RemoveCertificate(string thumbprint)
{
try
{
- _logger.LogTrace($"Removing Certificate with Alias: {config.JobCertificate.Alias}");
- ClientPSCertStoreManager manager = new ClientPSCertStoreManager(_logger, myRunspace, config.JobHistoryId);
- manager.RemoveCertificate(config.JobCertificate.Alias, config.CertificateStoreDetails.StorePath);
- _logger.LogTrace($"Removed Certificate with Alias: {config.JobCertificate.Alias}");
+ using (_psHelper)
+ {
+ _psHelper.Initialize();
+
+ _logger.LogTrace($"Attempting to remove thumbprint {thumbprint} from store {_storePath}");
+
+ var parameters = new Dictionary()
+ {
+ { "Thumbprint", thumbprint },
+ { "StorePath", _storePath }
+ };
+
+ _psHelper.ExecutePowerShell("Remove-KFCertificateFromStore", parameters);
+ _logger.LogTrace("Returned from executing PS function (Remove-KFCertificateFromStore)");
+
+ _psHelper.Terminate();
+ }
return new JobResult
{
Result = OrchestratorJobStatusJobResult.Success,
- JobHistoryId = config.JobHistoryId,
+ JobHistoryId = _jobHistoryID,
FailureMessage = ""
};
}
- catch (Exception e)
+ catch (Exception ex)
{
+ var failureMessage = $"Management job {_operationType} failed on Store '{_storePath}' on server '{_clientMachineName}' with error: '{LogHandler.FlattenException(ex)}'";
+ _logger.LogWarning(failureMessage);
+
return new JobResult
{
Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = config.JobHistoryId,
- FailureMessage = $"Error Occurred while attempting to remove certificate: {LogHandler.FlattenException(e)}"
+ JobHistoryId = _jobHistoryID,
+ FailureMessage = failureMessage
};
}
}
diff --git a/IISU/ImplementedStoreTypes/Win/ReEnrollment.cs b/IISU/ImplementedStoreTypes/Win/ReEnrollment.cs
index 8f0c4de..0b5610f 100644
--- a/IISU/ImplementedStoreTypes/Win/ReEnrollment.cs
+++ b/IISU/ImplementedStoreTypes/Win/ReEnrollment.cs
@@ -11,6 +11,9 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
+
+// 021225 rcp 2.6.0 Cleaned up and verified code
+
using Keyfactor.Logging;
using Keyfactor.Orchestrators.Extensions;
using Keyfactor.Orchestrators.Extensions.Interfaces;
diff --git a/IISU/ImplementedStoreTypes/Win/WinCertCertificateInfo.cs b/IISU/ImplementedStoreTypes/Win/WinCertCertificateInfo.cs
new file mode 100644
index 0000000..27d5e00
--- /dev/null
+++ b/IISU/ImplementedStoreTypes/Win/WinCertCertificateInfo.cs
@@ -0,0 +1,31 @@
+// Copyright 2023 Keyfactor
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// 021225 rcp 2.6.0 Cleaned up and verified code
+
+namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.WinCert
+{
+ public class WinCertCertificateInfo
+ {
+ public string StoreName { get; set; }
+ public string Certificate { get; set; }
+ public string ExpiryDate { get; set; }
+ public string Issuer { get; set; }
+ public string Thumbprint { get; set; }
+ public bool HasPrivateKey { get; set; }
+ public string SAN { get; set; }
+ public string ProviderName { get; set; }
+ public string Base64Data { get; set; }
+ }
+}
diff --git a/IISU/ImplementedStoreTypes/Win/WinInventory.cs b/IISU/ImplementedStoreTypes/Win/WinInventory.cs
index 99954e6..0cae7b8 100644
--- a/IISU/ImplementedStoreTypes/Win/WinInventory.cs
+++ b/IISU/ImplementedStoreTypes/Win/WinInventory.cs
@@ -11,11 +11,14 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
+using Keyfactor.Logging;
using Keyfactor.Orchestrators.Common.Enums;
using Keyfactor.Orchestrators.Extensions;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Text;
@@ -29,11 +32,41 @@ public WinInventory(ILogger logger) : base(logger)
_logger = logger;
}
+ public List GetInventoryItems(RemoteSettings settings, string storePath)
+ {
+ _logger.LogTrace("Entering WinCert GetInventoryItems.");
+ List inventoryItems = new List();
+
+ _logger.LogTrace($"Attempting to read certificates from store: {storePath}.");
+ foreach (Certificate cert in base.GetCertificatesFromStore(settings, storePath))
+ {
+ var entryParms = new Dictionary
+ {
+ { "ProviderName", cert.CryptoServiceProvider },
+ { "SAN", cert.SAN }
+ };
+
+ inventoryItems.Add(new CurrentInventoryItem
+ {
+ Certificates = new[] { cert.CertificateData },
+ Alias = cert.Thumbprint,
+ PrivateKeyEntry = cert.HasPrivateKey,
+ UseChainLevel = false,
+ ItemStatus = OrchestratorInventoryItemStatus.Unknown,
+ Parameters = entryParms
+ });
+ }
+
+ _logger.LogTrace($"Found {inventoryItems.Count} certificates. Exiting WinCert GetInventoryItems.");
+ return inventoryItems;
+ }
+
public List GetInventoryItems(Runspace runSpace, string storePath)
{
_logger.LogTrace("Entering WinCert GetInventoryItems.");
List inventoryItems = new List();
+
foreach (Certificate cert in base.GetCertificatesFromStore(runSpace, storePath))
{
var entryParms = new Dictionary
diff --git a/IISU/ImplementedStoreTypes/WinIIS/IISBindingInfo.cs b/IISU/ImplementedStoreTypes/WinIIS/IISBindingInfo.cs
new file mode 100644
index 0000000..b7711cd
--- /dev/null
+++ b/IISU/ImplementedStoreTypes/WinIIS/IISBindingInfo.cs
@@ -0,0 +1,69 @@
+// Copyright 2025 Keyfactor
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Ignore Spelling: Keyfactor IISU
+
+// 021225 rcp 2.6.0 Cleaned up and verified code
+
+using System;
+using System.Collections.Generic;
+
+namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.IISU
+{
+ public class IISBindingInfo
+ {
+ public string SiteName { get; set; }
+ public string Protocol { get; set; }
+ public string IPAddress { get; set; }
+ public string Port { get; set; }
+ public string? HostName { get; set; }
+ public string SniFlag { get; set; }
+
+ public IISBindingInfo(Dictionary bindingInfo)
+ {
+ SiteName = bindingInfo["SiteName"].ToString();
+ Protocol = bindingInfo["Protocol"].ToString();
+ IPAddress = bindingInfo["IPAddress"].ToString();
+ Port = bindingInfo["Port"].ToString();
+ HostName = bindingInfo["HostName"]?.ToString();
+ SniFlag = MigrateSNIFlag(bindingInfo["SniFlag"].ToString());
+ }
+
+ private string MigrateSNIFlag(string input)
+ {
+ // Check if the input is numeric, if so, just return it as an integer
+ if (int.TryParse(input, out int numericValue))
+ {
+ return numericValue.ToString();
+ }
+
+ if (string.IsNullOrEmpty(input)) { throw new ArgumentNullException("SNI/SSL Flag", "The SNI or SSL Flag flag must not be empty or null."); }
+
+ // Handle the string cases
+ switch (input.ToLower())
+ {
+ case "0 - no sni":
+ return "0";
+ case "1 - sni enabled":
+ return "1";
+ case "2 - non sni binding":
+ return "2";
+ case "3 - sni binding":
+ return "3";
+ default:
+ throw new ArgumentOutOfRangeException($"Received an invalid value '{input}' for sni/ssl Flag value");
+ }
+ }
+ }
+}
diff --git a/IISU/ImplementedStoreTypes/WinIIS/IISCertificateInfo.cs b/IISU/ImplementedStoreTypes/WinIIS/IISCertificateInfo.cs
new file mode 100644
index 0000000..4146468
--- /dev/null
+++ b/IISU/ImplementedStoreTypes/WinIIS/IISCertificateInfo.cs
@@ -0,0 +1,41 @@
+// Copyright 2025 Keyfactor
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// 021225 rcp 2.6.0 Cleaned up and verified code
+
+using System;
+
+namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.IISU
+{
+ public class IISCertificateInfo
+ {
+ public string SiteName { get; set; } //
+ public string Binding { get; set; } //
+ public string IPAddress { get; set; } //
+ public string Port { get; set; } //
+ public string Protocol { get; set; } //
+ public string HostName { get; set; } //
+ public string SNI { get; set; } //
+ public string Certificate { get; set; } //
+ public DateTime ExpiryDate { get; set; } //
+ public string Issuer { get; set; } //
+ public string Thumbprint { get; set; } //
+ public bool HasPrivateKey { get; set; } //
+ public string SAN { get; set; } //
+ public string ProviderName { get; set; } //
+ public string CertificateBase64 { get; set; } //
+ public string FriendlyName { get; set; } //
+
+ }
+}
diff --git a/IISU/ImplementedStoreTypes/WinIIS/Inventory.cs b/IISU/ImplementedStoreTypes/WinIIS/Inventory.cs
index 0255948..f0a46d2 100644
--- a/IISU/ImplementedStoreTypes/WinIIS/Inventory.cs
+++ b/IISU/ImplementedStoreTypes/WinIIS/Inventory.cs
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// 021225 rcp 2.6.0 Cleaned up and verified code
+
using Keyfactor.Logging;
using Keyfactor.Orchestrators.Common.Enums;
using Keyfactor.Orchestrators.Extensions;
@@ -20,15 +22,22 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Management.Automation;
namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.IISU
{
public class Inventory : WinCertJobTypeBase, IInventoryJobExtension
{
private ILogger _logger;
+ Collection? results = null;
public string ExtensionName => "WinIISUInventory";
+ public Inventory()
+ {
+
+ }
public Inventory(IPAMSecretResolver resolver)
{
_resolver = resolver;
@@ -39,54 +48,49 @@ public JobResult ProcessJob(InventoryJobConfiguration jobConfiguration, SubmitIn
_logger = LogHandler.GetClassLogger();
_logger.MethodEntry();
-
- return PerformInventory(jobConfiguration, submitInventoryUpdate);
- }
-
- private JobResult PerformInventory(InventoryJobConfiguration config, SubmitInventoryUpdate submitInventory)
- {
try
{
var inventoryItems = new List();
- string myConfig = config.ToString();
+ string myConfig = jobConfiguration.ToString();
- _logger.LogTrace(JobConfigurationParser.ParseInventoryJobConfiguration(config));
+ _logger.LogTrace(JobConfigurationParser.ParseInventoryJobConfiguration(jobConfiguration));
- string serverUserName = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server UserName", config.ServerUsername);
- string serverPassword = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server Password", config.ServerPassword);
+ string serverUserName = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server UserName", jobConfiguration.ServerUsername);
+ string serverPassword = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server Password", jobConfiguration.ServerPassword);
// Deserialize specific job properties
- var jobProperties = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
+ var jobProperties = JsonConvert.DeserializeObject(jobConfiguration.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
string protocol = jobProperties.WinRmProtocol;
string port = jobProperties.WinRmPort;
bool IncludePortInSPN = jobProperties.SpnPortFlag;
- string clientMachineName = config.CertificateStoreDetails.ClientMachine;
- string storePath = config.CertificateStoreDetails.StorePath;
+ string clientMachineName = jobConfiguration.CertificateStoreDetails.ClientMachine;
+ string storePath = jobConfiguration.CertificateStoreDetails.StorePath;
if (storePath != null)
{
- _logger.LogTrace($"Establishing runspace on client machine: {clientMachineName}");
- using var myRunspace = PsHelper.GetClientPsRunspace(protocol, clientMachineName, port, IncludePortInSPN, serverUserName, serverPassword);
- myRunspace.Open();
+ _logger.LogTrace($"Getting settings to connect to: {clientMachineName}");
- _logger.LogTrace("Runspace is now open");
- _logger.LogTrace($"Attempting to read bound IIS certificates from cert store: {storePath}");
- WinIISInventory IISInventory = new WinIISInventory(_logger);
- inventoryItems = IISInventory.GetInventoryItems(myRunspace, storePath);
+ // Create the remote connection class to pass to Inventory Class
+ RemoteSettings settings = new();
+ settings.ClientMachineName = jobConfiguration.CertificateStoreDetails.ClientMachine;
+ settings.Protocol = jobProperties.WinRmProtocol;
+ settings.Port = jobProperties.WinRmPort;
+ settings.IncludePortInSPN = jobProperties.SpnPortFlag;
+ settings.ServerUserName = serverUserName;
+ settings.ServerPassword = serverPassword;
- _logger.LogTrace($"A total of {inventoryItems.Count} bound certificate(s) were found");
- _logger.LogTrace("Closing runspace...");
- myRunspace.Close();
+ _logger.LogTrace("Querying IIS Inventory..");
+ inventoryItems = QueryIISCertificates(settings);
_logger.LogTrace("Invoking submitInventory..");
- submitInventory.Invoke(inventoryItems);
+ submitInventoryUpdate.Invoke(inventoryItems);
_logger.LogTrace($"submitInventory Invoked... {inventoryItems.Count} Items");
return new JobResult
{
Result = OrchestratorJobStatusJobResult.Success,
- JobHistoryId = config.JobHistoryId,
+ JobHistoryId = jobConfiguration.JobHistoryId,
FailureMessage = ""
};
}
@@ -94,36 +98,85 @@ private JobResult PerformInventory(InventoryJobConfiguration config, SubmitInven
return new JobResult
{
Result = OrchestratorJobStatusJobResult.Warning,
- JobHistoryId = config.JobHistoryId,
+ JobHistoryId = jobConfiguration.JobHistoryId,
FailureMessage =
$"No certificates were found in the Certificate Store Path: {storePath} on server: {clientMachineName}"
};
}
- catch (CertificateStoreException psEx)
- {
- _logger.LogTrace(psEx.Message);
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = config.JobHistoryId,
- FailureMessage =
- $"Unable to open remote certificate store: {LogHandler.FlattenException(psEx)}"
- };
- }
catch (Exception ex)
{
_logger.LogTrace(LogHandler.FlattenException(ex));
- var failureMessage = $"Inventory job failed for Site '{config.CertificateStoreDetails.StorePath}' on server '{config.CertificateStoreDetails.ClientMachine}' with error: '{LogHandler.FlattenException(ex)}'";
+ var failureMessage = $"Inventory job failed for Site '{jobConfiguration.CertificateStoreDetails.StorePath}' on server '{jobConfiguration.CertificateStoreDetails.ClientMachine}' with error: '{LogHandler.FlattenException(ex)}'";
_logger.LogWarning(failureMessage);
return new JobResult
{
Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = config.JobHistoryId,
+ JobHistoryId = jobConfiguration.JobHistoryId,
FailureMessage = failureMessage
};
}
}
+
+ public List QueryIISCertificates(RemoteSettings settings)
+ {
+ List Inventory = new();
+
+ using (PSHelper ps = new(settings.Protocol, settings.Port, settings.IncludePortInSPN, settings.ClientMachineName, settings.ServerUserName, settings.ServerPassword))
+ {
+ ps.Initialize();
+
+ //if (ps.IsLocalMachine)
+ //{
+ // _logger.LogTrace("Executing function locally");
+ // results = ps.ExecutePowerShell("Get-KFIISBoundCertificates");
+ //}
+ //else
+ //{
+ // _logger.LogTrace("Executing function remotely");
+ // results = ps.InvokeFunction("Get-KFIISBoundCertificates");
+ //}
+
+ results = ps.ExecutePowerShell("Get-KFIISBoundCertificates");
+
+ // If there are certificates, deserialize the results and send them back to command
+ if (results != null && results.Count > 0)
+ {
+ var jsonResults = results[0].ToString();
+ var certInfoList = Certificate.Utilities.DeserializeCertificates(jsonResults); // JsonConvert.DeserializeObject>(jsonResults);
+
+ foreach (IISCertificateInfo cert in certInfoList)
+ {
+ var siteSettingsDict = new Dictionary
+ {
+ { "SiteName", cert.SiteName },
+ { "Port", cert.Port },
+ { "IPAddress", cert.IPAddress },
+ { "HostName", cert.HostName },
+ { "SniFlag", cert.SNI },
+ { "Protocol", cert.Protocol },
+ { "ProviderName", cert.ProviderName },
+ { "SAN", cert.SAN }
+ };
+
+ Inventory.Add(
+ new CurrentInventoryItem
+ {
+ Certificates = new[] {cert.CertificateBase64 },
+ Alias = cert.Thumbprint + ":" + cert.Binding?.ToString(),
+ PrivateKeyEntry = cert.HasPrivateKey,
+ UseChainLevel = false,
+ ItemStatus = OrchestratorInventoryItemStatus.Unknown,
+ Parameters = siteSettingsDict
+ }
+ );
+ }
+ }
+ ps.Terminate();
+ }
+
+ return Inventory;
+ }
}
}
\ No newline at end of file
diff --git a/IISU/ImplementedStoreTypes/WinIIS/Management.cs b/IISU/ImplementedStoreTypes/WinIIS/Management.cs
index f917eb7..ac33c4c 100644
--- a/IISU/ImplementedStoreTypes/WinIIS/Management.cs
+++ b/IISU/ImplementedStoreTypes/WinIIS/Management.cs
@@ -13,28 +13,33 @@
// limitations under the License.
using System;
-using System.IO;
-using System.Linq;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Management.Automation;
-using System.Management.Automation.Runspaces;
-using System.Net;
using Keyfactor.Logging;
using Keyfactor.Orchestrators.Common.Enums;
using Keyfactor.Orchestrators.Extensions;
using Keyfactor.Orchestrators.Extensions.Interfaces;
using Microsoft.Extensions.Logging;
-using Microsoft.PowerShell.Commands;
using Newtonsoft.Json;
namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.IISU
{
public class Management : WinCertJobTypeBase, IManagementJobExtension
{
+ public string ExtensionName => "WinIISUManagement";
private ILogger _logger;
- public string ExtensionName => "WinIISUManagement";
+ private PSHelper _psHelper;
+ private Collection? _results = null;
- private Runspace myRunspace;
+ // Function wide config values
+ private string _clientMachineName = string.Empty;
+ private string _storePath = string.Empty;
+ private long _jobHistoryID = 0;
+ private CertStoreOperationType _operationType;
+
+ //private Runspace myRunspace;
public Management(IPAMSecretResolver resolver)
{
@@ -57,20 +62,6 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
_logger.LogTrace(e.Message);
}
- string serverUserName = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server UserName", config.ServerUsername);
- string serverPassword = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server Password", config.ServerPassword);
-
- var jobProperties = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
- string protocol = jobProperties.WinRmProtocol;
- string port = jobProperties.WinRmPort;
- bool IncludePortInSPN = jobProperties.SpnPortFlag;
- string clientMachineName = config.CertificateStoreDetails.ClientMachine;
- string storePath = config.CertificateStoreDetails.StorePath;
- long JobHistoryID = config.JobHistoryId;
-
- _logger.LogTrace($"Establishing runspace on client machine: {clientMachineName}");
- myRunspace = PsHelper.GetClientPsRunspace(protocol, clientMachineName, port, IncludePortInSPN, serverUserName, serverPassword);
-
var complete = new JobResult
{
Result = OrchestratorJobStatusJobResult.Failure,
@@ -78,139 +69,204 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
"Invalid Management Operation"
};
- switch (config.OperationType)
- {
- case CertStoreOperationType.Add:
- _logger.LogTrace("Entering Add...");
+ // Start parsing config information and establishing PS Session
+ _jobHistoryID = config.JobHistoryId;
+ _storePath = config.CertificateStoreDetails.StorePath;
+ _clientMachineName = config.CertificateStoreDetails.ClientMachine;
+ _operationType = config.OperationType;
+
+ var jobProperties = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
+
+ string serverUserName = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server UserName", config.ServerUsername);
+ string serverPassword = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server Password", config.ServerPassword);
- myRunspace.Open();
- complete = PerformAddCertificate(config, serverUserName, serverPassword);
- myRunspace.Close();
+ string protocol = jobProperties?.WinRmProtocol;
+ string port = jobProperties?.WinRmPort;
+ bool includePortInSPN = (bool)jobProperties?.SpnPortFlag;
- _logger.LogTrace("After Perform Addition...");
- break;
- case CertStoreOperationType.Remove:
- _logger.LogTrace("Entering Remove...");
+ _psHelper = new(protocol, port, includePortInSPN, _clientMachineName, serverUserName, serverPassword);
- _logger.LogTrace("After PerformRemoval...");
- myRunspace.Open();
- complete = PerformRemoveCertificate(config, serverUserName, serverPassword);
- myRunspace.Close();
+ _psHelper.Initialize();
- _logger.LogTrace("After Perform Removal...");
- break;
+ using (_psHelper)
+ {
+ switch (_operationType)
+ {
+ case CertStoreOperationType.Add:
+ {
+ string certificateContents = config.JobCertificate.Contents;
+ string privateKeyPassword = config.JobCertificate.PrivateKeyPassword;
+ string? cryptoProvider = config.JobProperties["ProviderName"]?.ToString();
+
+ // Add Certificate to Cert Store
+ try
+ {
+ string newThumbprint = AddCertificate(certificateContents, privateKeyPassword, cryptoProvider);
+ _logger.LogTrace($"Completed adding the certificate to the store");
+
+ // Bind Certificate to IIS Site
+ if (newThumbprint != null)
+ {
+ IISBindingInfo bindingInfo = new IISBindingInfo(config.JobProperties);
+ WinIISBinding.BindCertificate(_psHelper, bindingInfo, newThumbprint, "", _storePath);
+
+ complete = new JobResult
+ {
+ Result = OrchestratorJobStatusJobResult.Success,
+ JobHistoryId = _jobHistoryID,
+ FailureMessage = ""
+ };
+ }
+ }
+ catch (Exception ex)
+ {
+ return new JobResult
+ {
+ Result = OrchestratorJobStatusJobResult.Failure,
+ JobHistoryId = _jobHistoryID,
+ FailureMessage = ex.Message
+ };
+ }
+
+ _logger.LogTrace($"Completed adding and binding the certificate to the store");
+
+ break;
+ }
+ case CertStoreOperationType.Remove:
+ {
+ // Removing a certificate involves two steps: UnBind the certificate, then delete the cert from the store
+
+ string thumbprint = config.JobCertificate.Alias.Split(':')[0];
+ try
+ {
+ if (WinIISBinding.UnBindCertificate(_psHelper, new IISBindingInfo(config.JobProperties)))
+ {
+ complete = RemoveCertificate(thumbprint);
+ }
+ }
+ catch (Exception ex)
+ {
+ return new JobResult
+ {
+ Result = OrchestratorJobStatusJobResult.Failure,
+ JobHistoryId = _jobHistoryID,
+ FailureMessage = ex.Message
+ };
+ }
+
+ _logger.LogTrace($"Completed removing the certificate from the store");
+
+ break;
+ }
+ }
}
- _logger.MethodExit();
return complete;
+
}
catch (Exception ex)
{
- _logger.LogTrace(LogHandler.FlattenException(ex));
-
- var failureMessage = $"Management job {config.OperationType} failed for Site '{config.CertificateStoreDetails.StorePath}' on server '{config.CertificateStoreDetails.ClientMachine}' with error: '{LogHandler.FlattenException(ex)}'";
- _logger.LogWarning(failureMessage);
+ _logger.LogTrace(ex.Message);
return new JobResult
{
Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = config.JobHistoryId,
- FailureMessage = failureMessage
+ JobHistoryId = _jobHistoryID,
+ FailureMessage = ex.Message
};
}
+ finally
+ {
+ _psHelper.Terminate();
+ _logger.MethodExit();
+ }
}
- private JobResult PerformAddCertificate(ManagementJobConfiguration config, string serverUsername, string serverPassword)
+ public string AddCertificate(string certificateContents, string privateKeyPassword, string cryptoProvider)
{
try
{
-#nullable enable
- string certificateContents = config.JobCertificate.Contents;
- string privateKeyPassword = config.JobCertificate.PrivateKeyPassword;
- string storePath = config.CertificateStoreDetails.StorePath;
- long jobNumber = config.JobHistoryId;
- string? cryptoProvider = config.JobProperties["ProviderName"]?.ToString();
-#nullable disable
-
- // If a crypto provider was provided, check to see if it exists
- if (cryptoProvider != null)
- {
- _logger.LogInformation($"Checking the server for the crypto provider: {cryptoProvider}");
- if (!PsHelper.IsCSPFound(PsHelper.GetCSPList(myRunspace), cryptoProvider))
- { throw new Exception($"The Crypto Profider: {cryptoProvider} was not found. Please check the spelling and accuracy of the Crypto Provider Name provided. If unsure which provider to use, leave the field blank and the default crypto provider will be used."); }
- }
-
- if (storePath != null)
- {
- _logger.LogInformation($"Attempting to add IISU certificate to cert store: {storePath}");
- }
+ string newThumbprint = string.Empty;
- ClientPSCertStoreManager manager = new ClientPSCertStoreManager(_logger, myRunspace, jobNumber);
+ _logger.LogTrace("Attempting to execute PS function (Add-KFCertificateToStore)");
- // This method is retired
- //JobResult result = manager.AddCertificate(certificateContents, privateKeyPassword, storePath);
-
- // Write the certificate contents to a temporary file on the remote computer, returning the filename.
- string filePath = manager.CreatePFXFile(certificateContents, privateKeyPassword);
- _logger.LogTrace($"{filePath} was created.");
+ // Mandatory parameters
+ var parameters = new Dictionary
+ {
+ { "Base64Cert", certificateContents },
+ { "StoreName", _storePath },
+ };
- // Using certutil on the remote computer, import the pfx file using a supplied csp if any.
- JobResult result = manager.ImportPFXFile(filePath, privateKeyPassword, cryptoProvider, storePath);
+ // Optional parameters
+ if (!string.IsNullOrEmpty(privateKeyPassword)) { parameters.Add("PrivateKeyPassword", privateKeyPassword); }
+ if (!string.IsNullOrEmpty(cryptoProvider)) { parameters.Add("CryptoServiceProvider", cryptoProvider); }
- // Delete the temporary file
- manager.DeletePFXFile(Path.GetDirectoryName(filePath), Path.GetFileNameWithoutExtension(filePath));
+ _results = _psHelper.ExecutePowerShell("Add-KFCertificateToStore", parameters);
+ _logger.LogTrace("Returned from executing PS function (Add-KFCertificateToStore)");
- if (result.Result == OrchestratorJobStatusJobResult.Success)
+ // This should return the thumbprint of the certificate
+ if (_results != null && _results.Count > 0)
{
- // Bind to IIS
- _logger.LogInformation("Attempting to bind certificate to website.");
- ClientPSIIManager iisManager = new ClientPSIIManager(config, serverUsername, serverPassword);
- result = iisManager.BindCertificate(manager.X509Cert);
-
- // Provide logging information
- if (result.Result == OrchestratorJobStatusJobResult.Success) { _logger.LogInformation("Certificate was successfully bound to the website."); }
- else { _logger.LogInformation("There was an issue while attempting to bind the certificate to the website. Check the logs for more information."); }
-
- return result;
+ newThumbprint = _results[0].ToString();
+ _logger.LogTrace($"Added certificate to store {_storePath}, returned with the thumbprint {newThumbprint}");
+ }
+ else
+ {
+ _logger.LogTrace("No results were returned. There could have been an error while adding the certificate. Look in the trace logs for PowerShell information.");
}
- else return result;
+
+ return newThumbprint;
}
- catch (Exception e)
+ catch (Exception ex)
{
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = config.JobHistoryId,
- FailureMessage =
- $"Management/Add {e.Message}"
- };
+ var failureMessage = $"Management job {_operationType} failed on Store '{_storePath}' on server '{_clientMachineName}' with error: '{LogHandler.FlattenException(ex)}'";
+ _logger.LogError(failureMessage);
+
+ throw new Exception (failureMessage);
}
- }
+}
- private JobResult PerformRemoveCertificate(ManagementJobConfiguration config, string serverUsername, string serverPassword)
+ public JobResult RemoveCertificate(string thumbprint)
{
- _logger.LogTrace("Before Remove Certificate...");
+ try
+ {
+ using (_psHelper)
+ {
+ _psHelper.Initialize();
- string storePath = config.CertificateStoreDetails.StorePath;
- long jobNumber = config.JobHistoryId;
+ _logger.LogTrace($"Attempting to remove thumbprint {thumbprint} from store {_storePath}");
- // First we need to unbind the certificate from IIS before we remove it from the store
- ClientPSIIManager iisManager = new ClientPSIIManager(config, serverUsername, serverPassword);
- JobResult result = iisManager.UnBindCertificate();
+ var parameters = new Dictionary()
+ {
+ { "Thumbprint", thumbprint },
+ { "StorePath", _storePath }
+ };
- if (result.Result == OrchestratorJobStatusJobResult.Success)
- {
- ClientPSCertStoreManager manager = new ClientPSCertStoreManager(_logger, myRunspace, jobNumber);
- manager.RemoveCertificate(config.JobCertificate.Alias, storePath);
+ _psHelper.ExecutePowerShell("Remove-KFCertificateFromStore", parameters);
+ _logger.LogTrace("Returned from executing PS function (Remove-KFCertificateFromStore)");
+
+ _psHelper.Terminate();
+ }
return new JobResult
{
Result = OrchestratorJobStatusJobResult.Success,
- JobHistoryId = config.JobHistoryId,
+ JobHistoryId = _jobHistoryID,
FailureMessage = ""
};
}
- else return result;
+ catch (Exception ex)
+ {
+ var failureMessage = $"Management job {_operationType} failed on Store '{_storePath}' on server '{_clientMachineName}' with error: '{LogHandler.FlattenException(ex)}'";
+ _logger.LogWarning(failureMessage);
+
+ return new JobResult
+ {
+ Result = OrchestratorJobStatusJobResult.Failure,
+ JobHistoryId = _jobHistoryID,
+ FailureMessage = failureMessage
+ };
+ }
}
}
}
\ No newline at end of file
diff --git a/IISU/ImplementedStoreTypes/WinIIS/ReEnrollment.cs b/IISU/ImplementedStoreTypes/WinIIS/ReEnrollment.cs
index 109b554..e96fbe9 100644
--- a/IISU/ImplementedStoreTypes/WinIIS/ReEnrollment.cs
+++ b/IISU/ImplementedStoreTypes/WinIIS/ReEnrollment.cs
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// 021225 rcp 2.6.0 Cleaned up and verified code
+
using Keyfactor.Logging;
using Keyfactor.Orchestrators.Extensions;
using Keyfactor.Orchestrators.Extensions.Interfaces;
diff --git a/IISU/ImplementedStoreTypes/WinIIS/WinIISBinding.cs b/IISU/ImplementedStoreTypes/WinIIS/WinIISBinding.cs
new file mode 100644
index 0000000..4988ada
--- /dev/null
+++ b/IISU/ImplementedStoreTypes/WinIIS/WinIISBinding.cs
@@ -0,0 +1,120 @@
+// Copyright 2025 Keyfactor
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// 021225 rcp 2.6.0 Cleaned up and verified code
+
+using Keyfactor.Logging;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Management.Automation;
+
+namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.IISU
+{
+ public class WinIISBinding
+ {
+ private static ILogger _logger;
+ private static Collection? _results = null;
+ private static PSHelper _helper;
+
+ public static void BindCertificate(PSHelper psHelper, IISBindingInfo bindingInfo, string thumbprint, string renewalThumbprint, string storePath)
+ {
+ _logger = LogHandler.GetClassLogger(typeof(WinIISBinding));
+ _logger.LogTrace("Attempting to bind and execute PS function (New-KFIISSiteBinding)");
+
+ // Mandatory parameters
+ var parameters = new Dictionary
+ {
+ { "SiteName", bindingInfo.SiteName },
+ { "IPAddress", bindingInfo.IPAddress },
+ { "Port", bindingInfo.Port },
+ { "Protocol", bindingInfo.Protocol },
+ { "Thumbprint", thumbprint },
+ { "StoreName", storePath },
+ { "SslFlags", bindingInfo.SniFlag }
+ };
+
+ // Optional parameters
+ if (!string.IsNullOrEmpty(bindingInfo.HostName)) { parameters.Add("HostName", bindingInfo.HostName); }
+
+ _results = psHelper.ExecutePowerShell("New-KFIISSiteBinding", parameters); // returns true if successful
+ _logger.LogTrace("Returned from executing PS function (New-KFIISSiteBinding)");
+
+ // This should return the thumbprint of the certificate
+ if (_results != null && _results.Count > 0)
+ {
+ bool success = _results[0].BaseObject is bool value && value;
+ if (success)
+ {
+ _logger.LogTrace($"Bound certificate with the thumbprint: '{thumbprint}' to site: '{bindingInfo.SiteName}' successfully.");
+ return;
+ }
+ else
+ {
+ _logger.LogTrace("Something happened and the binding failed.");
+ }
+ }
+
+ throw new Exception($"An unknown error occurred while attempting to bind thumbprint: {thumbprint} to site: '{bindingInfo.SiteName}'. \nCheck the UO Logs for more information.");
+ }
+
+ public static bool UnBindCertificate(PSHelper psHelper, IISBindingInfo bindingInfo)
+ {
+ _logger.LogTrace("Attempting to UnBind and execute PS function (Remove-KFIISBinding)");
+
+ // Mandatory parameters
+ var parameters = new Dictionary
+ {
+ { "SiteName", bindingInfo.SiteName },
+ { "IPAddress", bindingInfo.IPAddress },
+ { "Port", bindingInfo.Port },
+ };
+
+ // Optional parameters
+ if (!string.IsNullOrEmpty(bindingInfo.HostName))
+ {
+ parameters.Add("HostName", bindingInfo.HostName);
+ }
+
+ try
+ {
+ var results = psHelper.ExecutePowerShell("Remove-KFIISBinding", parameters);
+ _logger.LogTrace("Returned from executing PS function (Remove-KFIISBinding)");
+
+ if (results == null || results.Count == 0)
+ {
+ _logger.LogWarning("PowerShell function returned no results.");
+ return false;
+ }
+
+ if (results[0].BaseObject is bool success)
+ {
+ _logger.LogTrace($"Returned from unbinding as {success}.");
+ return success;
+ }
+ else
+ {
+ _logger.LogWarning("Unexpected result type from PowerShell function.");
+ return false;
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "An error occurred while attempting to unbind the certificate.");
+ return false;
+ }
+ }
+ }
+}
diff --git a/IISU/ImplementedStoreTypes/WinIIS/WinIISInventory.cs b/IISU/ImplementedStoreTypes/WinIIS/WinIISInventory.cs
deleted file mode 100644
index 0ce7320..0000000
--- a/IISU/ImplementedStoreTypes/WinIIS/WinIISInventory.cs
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright 2022 Keyfactor
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-using Keyfactor.Logging;
-using Keyfactor.Orchestrators.Common.Enums;
-using Keyfactor.Orchestrators.Extensions;
-using Microsoft.Extensions.Logging;
-using Microsoft.PowerShell;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Management.Automation;
-using System.Management.Automation.Runspaces;
-using System.Text;
-
-namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.IISU
-{
- public class WinIISInventory : ClientPSCertStoreInventory
- {
- private ILogger _logger;
-
- public WinIISInventory()
- {
- _logger = LogHandler.GetClassLogger();
- }
-
- public WinIISInventory(ILogger logger) : base(logger)
- {
- _logger = logger;
- }
-
- public List GetInventoryItems(Runspace runSpace, string storePath)
- {
- _logger.LogTrace("Entering IISU GetInventoryItems");
- // Get the raw certificate inventory from cert store
- List certificates = base.GetCertificatesFromStore(runSpace, storePath);
-
- // Contains the inventory items to be sent back to KF
- List myBoundCerts = new List();
-
- using (PowerShell ps2 = PowerShell.Create())
- {
- ps2.Runspace = runSpace;
-
- if (runSpace.RunspaceIsRemote)
- {
- ps2.AddCommand("Import-Module")
- .AddParameter("Name", "WebAdministration")
- .AddStatement();
- }
- else
- {
- ps2.AddScript("Set-ExecutionPolicy RemoteSigned -Scope Process -Force");
- ps2.AddScript("Import-Module WebAdministration");
- }
-
- var searchScript = "Foreach($Site in get-website) { Foreach ($Bind in $Site.bindings.collection) {[pscustomobject]@{name=$Site.name;Protocol=$Bind.Protocol;Bindings=$Bind.BindingInformation;thumbprint=$Bind.certificateHash;sniFlg=$Bind.sslFlags}}}";
- ps2.AddScript(searchScript);
-
- _logger.LogTrace($"Attempting to initiate the following script:\n{searchScript}");
-
- var iisBindings = ps2.Invoke();
-
- if (ps2.HadErrors)
- {
- _logger.LogTrace("The previous script encountered errors. See below for more info.");
- string psError = string.Empty;
- try
- {
- psError = ps2.Streams.Error.ReadAll().Aggregate(String.Empty, (current, error) => current + (error.ErrorDetails?.Message ?? error.Exception.ToString()));
- }
- catch
- {
- }
-
- if (psError != null) { throw new Exception(psError); }
-
- }
-
- if (iisBindings.Count == 0)
- {
- _logger.LogTrace("No binding certificates were found. Exiting IISU GetInventoryItems.");
- return myBoundCerts;
- }
-
- //in theory should only be one, but keeping for future update to chance inventory
- foreach (var binding in iisBindings)
- {
- var thumbPrint = $"{(binding.Properties["thumbprint"]?.Value)}";
- if (string.IsNullOrEmpty(thumbPrint)) continue;
-
- Certificate foundCert = certificates.Find(m => m.Thumbprint.Equals(thumbPrint));
-
- if (foundCert == null) continue;
-
- var siteSettingsDict = new Dictionary
- {
- { "SiteName", binding.Properties["Name"]?.Value },
- { "Port", binding.Properties["Bindings"]?.Value.ToString()?.Split(':')[1] },
- { "IPAddress", binding.Properties["Bindings"]?.Value.ToString()?.Split(':')[0] },
- { "HostName", binding.Properties["Bindings"]?.Value.ToString()?.Split(':')[2] },
- { "SniFlag", binding.Properties["sniFlg"]?.Value.ToString() },
- { "Protocol", binding.Properties["Protocol"]?.Value },
- { "ProviderName", foundCert.CryptoServiceProvider },
- { "SAN", foundCert.SAN }
- };
-
- myBoundCerts.Add(
- new CurrentInventoryItem
- {
- Certificates = new[] { foundCert.CertificateData },
- Alias = thumbPrint + ":" + binding.Properties["Bindings"]?.Value.ToString(),
- PrivateKeyEntry = foundCert.HasPrivateKey,
- UseChainLevel = false,
- ItemStatus = OrchestratorInventoryItemStatus.Unknown,
- Parameters = siteSettingsDict
- }
- );
- }
- }
-
- _logger.LogTrace($"Found {myBoundCerts.Count} bound certificates. Exiting IISU GetInventoryItems.");
- return myBoundCerts;
- }
- }
-}
diff --git a/IISU/ImplementedStoreTypes/WinSQL/Inventory.cs b/IISU/ImplementedStoreTypes/WinSQL/Inventory.cs
index 31511a0..eb50428 100644
--- a/IISU/ImplementedStoreTypes/WinSQL/Inventory.cs
+++ b/IISU/ImplementedStoreTypes/WinSQL/Inventory.cs
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// Ignore Spelling: Keyfactor Sql
+
+// 021225 rcp 2.6.0 Cleaned up and verified code
+
using Keyfactor.Logging;
using Keyfactor.Orchestrators.Common.Enums;
using Keyfactor.Orchestrators.Extensions;
@@ -20,6 +24,8 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Management.Automation;
namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.WinSql
@@ -27,9 +33,15 @@ namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.WinSql
public class Inventory : WinCertJobTypeBase, IInventoryJobExtension
{
private ILogger _logger;
-
public string ExtensionName => "WinSqlInventory";
+ Collection? results = null;
+
+ public Inventory()
+ {
+
+ }
+
public Inventory(IPAMSecretResolver resolver)
{
_resolver = resolver;
@@ -40,103 +52,119 @@ public JobResult ProcessJob(InventoryJobConfiguration jobConfiguration, SubmitIn
_logger = LogHandler.GetClassLogger();
_logger.MethodEntry();
-
- return PerformInventory(jobConfiguration, submitInventoryUpdate);
- }
-
- private JobResult PerformInventory(InventoryJobConfiguration config, SubmitInventoryUpdate submitInventory)
- {
try
{
var inventoryItems = new List();
- string myConfig = config.ToString();
-
- _logger.LogTrace(JobConfigurationParser.ParseInventoryJobConfiguration(config));
+ _logger.LogTrace(JobConfigurationParser.ParseInventoryJobConfiguration(jobConfiguration));
- string serverUserName = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server UserName", config.ServerUsername);
- string serverPassword = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server Password", config.ServerPassword);
+ string serverUserName = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server UserName", jobConfiguration.ServerUsername);
+ string serverPassword = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server Password", jobConfiguration.ServerPassword);
// Deserialize specific job properties
- var jobProperties = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
+ var jobProperties = JsonConvert.DeserializeObject(jobConfiguration.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
string protocol = jobProperties.WinRmProtocol;
string port = jobProperties.WinRmPort;
bool IncludePortInSPN = jobProperties.SpnPortFlag;
- string clientMachineName = config.CertificateStoreDetails.ClientMachine;
- string storePath = config.CertificateStoreDetails.StorePath;
+ string clientMachineName = jobConfiguration.CertificateStoreDetails.ClientMachine;
+ string storePath = jobConfiguration.CertificateStoreDetails.StorePath;
if (storePath != null)
{
- _logger.LogTrace($"Establishing runspace on client machine: {clientMachineName}");
- using var myRunspace = PsHelper.GetClientPsRunspace(protocol, clientMachineName, port, IncludePortInSPN, serverUserName, serverPassword);
- myRunspace.Open();
+ // Create the remote connection class to pass to Inventory Class
+ RemoteSettings settings = new();
+ settings.ClientMachineName = jobConfiguration.CertificateStoreDetails.ClientMachine;
+ settings.Protocol = jobProperties.WinRmProtocol;
+ settings.Port = jobProperties.WinRmPort;
+ settings.IncludePortInSPN = jobProperties.SpnPortFlag;
+ settings.ServerUserName = serverUserName;
+ settings.ServerPassword = serverPassword;
- _logger.LogTrace("Runspace is now open");
_logger.LogTrace($"Attempting to read bound SQL Server certificates from cert store: {storePath}");
+ inventoryItems = QuerySQLCertificates(settings, storePath);
- SQLServerInventory sqlInventory = new SQLServerInventory(_logger);
- inventoryItems = sqlInventory.GetInventoryItems(myRunspace, config);
- if (inventoryItems != null)
- {
- _logger.LogTrace($"A total of {inventoryItems.Count} were found");
- _logger.LogTrace("Closing runspace...");
- myRunspace.Close();
-
- _logger.LogTrace("Invoking Inventory..");
- submitInventory.Invoke(inventoryItems);
- _logger.LogTrace($"Inventory Invoked... {inventoryItems.Count} Items");
-
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Success,
- JobHistoryId = config.JobHistoryId,
- FailureMessage = ""
- };
- }
- else
+ _logger.LogTrace("Invoking submitInventory..");
+ submitInventoryUpdate.Invoke(inventoryItems);
+ _logger.LogTrace($"submitInventory Invoked... {inventoryItems.Count} Items");
+
+ return new JobResult
{
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = config.JobHistoryId,
- FailureMessage = "Inventory Items was null, ensure sql server is installed on the machine."
- };
- }
+ Result = OrchestratorJobStatusJobResult.Success,
+ JobHistoryId = jobConfiguration.JobHistoryId,
+ FailureMessage = ""
+ };
}
return new JobResult
{
Result = OrchestratorJobStatusJobResult.Warning,
- JobHistoryId = config.JobHistoryId,
- FailureMessage =
- $"No certificates were found in the Certificate Store Path: {storePath} on server: {clientMachineName}"
- };
- }
- catch (CertificateStoreException psEx)
- {
- _logger.LogTrace(psEx.Message);
- return new JobResult
- {
- Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = config.JobHistoryId,
- FailureMessage =
- $"Unable to open remote certificate store: {LogHandler.FlattenException(psEx)}"
+ JobHistoryId = jobConfiguration.JobHistoryId,
+ FailureMessage = $"No certificates were found in the Certificate Store Path: {storePath} on server: {clientMachineName}"
};
}
+
catch (Exception ex)
{
_logger.LogTrace(LogHandler.FlattenException(ex));
- var failureMessage = $"Inventory job failed for Site '{config.CertificateStoreDetails.StorePath}' on server '{config.CertificateStoreDetails.ClientMachine}' with error: '{LogHandler.FlattenException(ex)}'";
+ var failureMessage = $"SQL Inventory job failed for Site '{jobConfiguration.CertificateStoreDetails.StorePath}' on server '{jobConfiguration.CertificateStoreDetails.ClientMachine}' with error: '{LogHandler.FlattenException(ex)}'";
_logger.LogWarning(failureMessage);
return new JobResult
{
Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = config.JobHistoryId,
+ JobHistoryId = jobConfiguration.JobHistoryId,
FailureMessage = failureMessage
};
}
}
+
+ public List QuerySQLCertificates(RemoteSettings settings, string storeName)
+ {
+ List Inventory = new();
+
+ using (PSHelper ps = new(settings.Protocol, settings.Port, settings.IncludePortInSPN, settings.ClientMachineName, settings.ServerUserName, settings.ServerPassword))
+ {
+ ps.Initialize();
+
+ var parameters = new Dictionary
+ {
+ { "StoreName", storeName }
+ };
+
+ results = ps.ExecutePowerShell("GET-KFSQLInventory", parameters);
+
+ // If there are certificates, deserialize the results and send them back to command
+ if (results != null && results.Count > 0)
+ {
+ var jsonResults = results[0].ToString();
+ var certInfoList = Certificate.Utilities.DeserializeCertificates(jsonResults); // JsonConvert.DeserializeObject>(jsonResults);
+
+ foreach (WinSQLCertificateInfo cert in certInfoList)
+ {
+ var siteSettingsDict = new Dictionary
+ {
+ { "InstanceName", cert.Parameters.InstanceName},
+ { "ProviderName", cert.Parameters.ProviderName}
+ };
+
+ Inventory.Add(
+ new CurrentInventoryItem
+ {
+ Certificates = new[] { cert.Certificates },
+ Alias = cert.Alias,
+ PrivateKeyEntry = cert.PrivateKeyEntry,
+ UseChainLevel = false,
+ ItemStatus = OrchestratorInventoryItemStatus.Unknown,
+ Parameters = siteSettingsDict
+ }
+ );
+ }
+ }
+ ps.Terminate();
+ }
+
+ return Inventory;
+ }
}
}
\ No newline at end of file
diff --git a/IISU/ImplementedStoreTypes/WinSQL/Management.cs b/IISU/ImplementedStoreTypes/WinSQL/Management.cs
index a4d2ffe..e5b1470 100644
--- a/IISU/ImplementedStoreTypes/WinSQL/Management.cs
+++ b/IISU/ImplementedStoreTypes/WinSQL/Management.cs
@@ -12,14 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// Ignore Spelling: thumbprint Keyfactor sql
+
+// 021225 rcp 2.6.0 Cleaned up and verified code
+
using System;
-using System.IO;
-using System.Management.Automation.Runspaces;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Management.Automation;
using Keyfactor.Logging;
using Keyfactor.Orchestrators.Common.Enums;
using Keyfactor.Orchestrators.Extensions;
using Keyfactor.Orchestrators.Extensions.Interfaces;
-using Microsoft.CodeAnalysis.CSharp;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
@@ -27,13 +31,21 @@ namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.WinSql
{
public class Management : WinCertJobTypeBase, IManagementJobExtension
{
+ public string ExtensionName => "WinSqlManagement";
private ILogger _logger;
- public string ExtensionName => "WinSqlManagement";
+ private PSHelper _psHelper;
+ private Collection? _results = null;
- private Runspace myRunspace;
+ // Function wide config values
+ private string _clientMachineName = string.Empty;
+ private string _storePath = string.Empty;
+ private long _jobHistoryID = 0;
+ private CertStoreOperationType _operationType;
- private string RenewalThumbprint;
+ private string RenewalThumbprint = string.Empty;
+ private string SQLInstanceNames = "MSSQLSERVER";
+ private bool RestartSQLService = false;
public Management(IPAMSecretResolver resolver)
{
@@ -56,47 +68,140 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
_logger.LogTrace(e.Message);
}
+ var complete = new JobResult
+ {
+ Result = OrchestratorJobStatusJobResult.Failure,
+ JobHistoryId = config.JobHistoryId,
+ FailureMessage = "Invalid Management Operation"
+ };
+
+ // Start parsing config information and establishing PS Session
+ _jobHistoryID = config.JobHistoryId;
+ _storePath = config.CertificateStoreDetails.StorePath;
+ _clientMachineName = config.CertificateStoreDetails.ClientMachine;
+ _operationType = config.OperationType;
+
string serverUserName = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server UserName", config.ServerUsername);
string serverPassword = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server Password", config.ServerPassword);
var jobProperties = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
- string protocol = jobProperties.WinRmProtocol;
- string port = jobProperties.WinRmPort;
- bool IncludePortInSPN = jobProperties.SpnPortFlag;
- string clientMachineName = config.CertificateStoreDetails.ClientMachine;
- string storePath = config.CertificateStoreDetails.StorePath;
- long JobHistoryID = config.JobHistoryId;
- _logger.LogTrace($"Establishing runspace on client machine: {clientMachineName}");
- myRunspace = PsHelper.GetClientPsRunspace(protocol, clientMachineName, port, IncludePortInSPN, serverUserName, serverPassword);
+ string protocol = jobProperties?.WinRmProtocol;
+ string port = jobProperties?.WinRmPort;
+ bool includePortInSPN = (bool)jobProperties?.SpnPortFlag;
+
+ RestartSQLService = jobProperties.RestartService;
- var complete = new JobResult
+ if (config.JobProperties.ContainsKey("InstanceName"))
{
- Result = OrchestratorJobStatusJobResult.Failure,
- FailureMessage =
- "Invalid Management Operation"
- };
+ SQLInstanceNames = config.JobProperties["InstanceName"]?.ToString() ?? "MSSQLSERVER";
+ }
+
+ if (config.JobProperties.ContainsKey("RenewalThumbprint"))
+ {
+ RenewalThumbprint = config.JobProperties["RenewalThumbprint"]?.ToString();
+ }
+
+ _psHelper = new(protocol, port, includePortInSPN, _clientMachineName, serverUserName, serverPassword);
+
+ _psHelper.Initialize();
- switch (config.OperationType)
+ using (_psHelper)
{
- case CertStoreOperationType.Add:
- _logger.LogTrace("Entering Add...");
- myRunspace.Open();
- complete = PerformAddCertificate(config, serverUserName, serverPassword);
- myRunspace.Close();
- _logger.LogTrace("After Perform Addition...");
- break;
- case CertStoreOperationType.Remove:
- _logger.LogTrace("Entering Remove...");
- _logger.LogTrace("After PerformRemoval...");
- myRunspace.Open();
- complete = PerformRemoveCertificate(config, serverUserName, serverPassword);
- myRunspace.Close();
- _logger.LogTrace("After Perform Removal...");
- break;
+ switch (_operationType)
+ {
+ case CertStoreOperationType.Add:
+ {
+ string certificateContents = config.JobCertificate.Contents;
+ string privateKeyPassword = config.JobCertificate.PrivateKeyPassword;
+ string cryptoProvider = config.JobProperties["ProviderName"]?.ToString() ?? string.Empty;
+
+ // Add Certificate to Cert Store
+ try
+ {
+ string newThumbprint = AddCertificate(certificateContents, privateKeyPassword, cryptoProvider);
+ _logger.LogTrace($"Completed adding the certificate to the store");
+
+ // Bind Certificate to SQL Instance
+ if (newThumbprint != null)
+ {
+ if (WinSqlBinding.BindSQLCertificate(_psHelper, SQLInstanceNames, newThumbprint, RenewalThumbprint, _storePath, RestartSQLService))
+ {
+ complete = new JobResult
+ {
+ Result = OrchestratorJobStatusJobResult.Success,
+ JobHistoryId = _jobHistoryID,
+ FailureMessage = ""
+ };
+ }
+ else
+ {
+ complete = new JobResult
+ {
+ Result = OrchestratorJobStatusJobResult.Failure,
+ JobHistoryId = _jobHistoryID,
+ FailureMessage = "Unable to Bind certificate to SQL Instance"
+ };
+ }
+
+ }
+ }
+ catch (Exception ex)
+ {
+ return new JobResult
+ {
+ Result = OrchestratorJobStatusJobResult.Failure,
+ JobHistoryId = _jobHistoryID,
+ FailureMessage = ex.Message
+ };
+ }
+
+ _logger.LogTrace($"Completed adding and binding the certificate to the store");
+
+ break;
+ }
+ case CertStoreOperationType.Remove:
+ {
+ try
+ {
+ // Unbind the certificates
+ _logger.LogTrace($"SQL Instance Names: {SQLInstanceNames}");
+ _logger.LogTrace("Attempting to unbind certificates.");
+ if (WinSqlBinding.UnBindSQLCertificate(_psHelper, SQLInstanceNames, RestartSQLService))
+ {
+ // Remove the certificate from the cert store
+ complete = RemoveCertificate(config.JobCertificate.Alias);
+ _logger.LogTrace($"Completed removing the certificate from the store");
+
+ break;
+ }
+ else
+ {
+ return new JobResult
+ {
+ Result = OrchestratorJobStatusJobResult.Failure,
+ JobHistoryId = _jobHistoryID,
+ FailureMessage = "Unable to unbind one or more certificates from the SQL Instances."
+ };
+ }
+
+ }
+ catch (Exception ex)
+ {
+ return new JobResult
+ {
+ Result = OrchestratorJobStatusJobResult.Failure,
+ JobHistoryId = _jobHistoryID,
+ FailureMessage = ex.Message
+ };
+ }
+
+ _logger.LogTrace($"Completed unbinding and removing the certificate from the store");
+ return complete;
+ }
+ }
}
- _logger.MethodExit();
return complete;
}
catch (Exception ex)
@@ -115,105 +220,91 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
}
}
- private JobResult PerformAddCertificate(ManagementJobConfiguration config, string serverUsername, string serverPassword)
+ public JobResult RemoveCertificate(string thumbprint)
{
- _logger.LogTrace("Before PerformAddition...");
-
try
{
-#nullable enable
- string certificateContents = config.JobCertificate.Contents;
- string privateKeyPassword = config.JobCertificate.PrivateKeyPassword;
- string storePath = config.CertificateStoreDetails.StorePath;
- long jobNumber = config.JobHistoryId;
- string? cryptoProvider = config.JobProperties["ProviderName"]?.ToString();
-#nullable disable
-
- // If a crypto provider was provided, check to see if it exists
- if (cryptoProvider != null)
- {
- _logger.LogInformation($"Checking the server for the crypto provider: {cryptoProvider}");
- if (!PsHelper.IsCSPFound(PsHelper.GetCSPList(myRunspace), cryptoProvider))
- { throw new Exception($"The Crypto Profider: {cryptoProvider} was not found. Please check the spelling and accuracy of the Crypto Provider Name provided. If unsure which provider to use, leave the field blank and the default crypto provider will be used."); }
- }
-
- if (storePath != null)
+ using (_psHelper)
{
- _logger.LogInformation($"Attempting to add WinSql certificate to cert store: {storePath}");
- }
-
- ClientPSCertStoreManager manager = new ClientPSCertStoreManager(_logger, myRunspace, jobNumber);
-
- // This method is retired
- //JobResult result = manager.AddCertificate(certificateContents, privateKeyPassword, storePath);
-
- // Write the certificate contents to a temporary file on the remote computer, returning the filename.
- string filePath = manager.CreatePFXFile(certificateContents, privateKeyPassword);
- _logger.LogTrace($"{filePath} was created.");
-
- // Using certutil on the remote computer, import the pfx file using a supplied csp if any.
- JobResult result = manager.ImportPFXFile(filePath, privateKeyPassword, cryptoProvider, storePath);
-
- // Delete the temporary file
- manager.DeletePFXFile(Path.GetDirectoryName(filePath), Path.GetFileNameWithoutExtension(filePath));
+ _psHelper.Initialize();
- if (result.Result == OrchestratorJobStatusJobResult.Success)
- {
+ _logger.LogTrace($"Attempting to remove thumbprint {thumbprint} from store {_storePath}");
- if (config.JobProperties.ContainsKey("RenewalThumbprint"))
+ var parameters = new Dictionary()
{
- RenewalThumbprint = config.JobProperties["RenewalThumbprint"].ToString();
- _logger.LogTrace($"Found Thumbprint Will Renew all Certs with this thumbprint: {RenewalThumbprint}");
- }
+ { "Thumbprint", thumbprint },
+ { "StorePath", _storePath }
+ };
- // Bind to SQL Server
- ClientPsSqlManager sqlManager = new ClientPsSqlManager(config, serverUsername, serverPassword);
- result = sqlManager.BindCertificates(RenewalThumbprint, manager.X509Cert);
+ _psHelper.ExecutePowerShell("Remove-KFCertificateFromStore", parameters);
+ _logger.LogTrace("Returned from executing PS function (Remove-KFCertificateFromStore)");
- // Provide logging information
- if (result.Result == OrchestratorJobStatusJobResult.Success) { _logger.LogInformation("Certificate was successfully bound to the SQL Server."); }
- else { _logger.LogInformation("There was an issue while attempting to bind the certificate to the SQL Server. Check the logs for more information."); }
-
- return result;
+ _psHelper.Terminate();
}
- else return result;
+
+ return new JobResult
+ {
+ Result = OrchestratorJobStatusJobResult.Success,
+ JobHistoryId = _jobHistoryID,
+ FailureMessage = ""
+ };
}
- catch (Exception e)
+ catch (Exception ex)
{
+ var failureMessage = $"Management job {_operationType} failed on Store '{_storePath}' on server '{_clientMachineName}' with error: '{LogHandler.FlattenException(ex)}'";
+ _logger.LogWarning(failureMessage);
+
return new JobResult
{
Result = OrchestratorJobStatusJobResult.Failure,
- JobHistoryId = config.JobHistoryId,
- FailureMessage =
- $"Management/Add {e.Message}"
+ JobHistoryId = _jobHistoryID,
+ FailureMessage = failureMessage
};
}
}
- private JobResult PerformRemoveCertificate(ManagementJobConfiguration config, string serverUsername, string serverPassword)
+ public string AddCertificate(string certificateContents, string privateKeyPassword, string cryptoProvider)
{
- _logger.LogTrace("Before Remove Certificate...");
+ try
+ {
+ string newThumbprint = string.Empty;
+
+ _logger.LogTrace("Attempting to execute PS function (Add-KFCertificateToStore)");
- string storePath = config.CertificateStoreDetails.StorePath;
- long jobNumber = config.JobHistoryId;
+ // Mandatory parameters
+ var parameters = new Dictionary
+ {
+ { "Base64Cert", certificateContents },
+ { "StoreName", _storePath },
+ };
- // Clear registry entry for SQL Server
- ClientPsSqlManager sqlManager = new ClientPsSqlManager(config, serverUsername, serverPassword);
- JobResult result = sqlManager.UnBindCertificate();
+ // Optional parameters
+ if (!string.IsNullOrEmpty(privateKeyPassword)) { parameters.Add("PrivateKeyPassword", privateKeyPassword); }
+ if (!string.IsNullOrEmpty(cryptoProvider)) { parameters.Add("CryptoServiceProvider", cryptoProvider); }
- if (result.Result == OrchestratorJobStatusJobResult.Success)
- {
- ClientPSCertStoreManager manager = new ClientPSCertStoreManager(_logger, myRunspace, jobNumber);
- manager.RemoveCertificate(config.JobCertificate.Alias, storePath);
+ _results = _psHelper.ExecutePowerShell("Add-KFCertificateToStore", parameters);
+ _logger.LogTrace("Returned from executing PS function (Add-KFCertificateToStore)");
- return new JobResult
+ // This should return the thumbprint of the certificate
+ if (_results != null && _results.Count > 0)
{
- Result = OrchestratorJobStatusJobResult.Success,
- JobHistoryId = config.JobHistoryId,
- FailureMessage = ""
- };
+ newThumbprint = _results[0].ToString();
+ _logger.LogTrace($"Added certificate to store {_storePath}, returned with the thumbprint {newThumbprint}");
+ }
+ else
+ {
+ _logger.LogTrace("No results were returned. There could have been an error while adding the certificate. Look in the trace logs for PowerShell information.");
+ }
+
+ return newThumbprint;
+ }
+ catch (Exception ex)
+ {
+ var failureMessage = $"Management job {_operationType} failed on Store '{_storePath}' on server '{_clientMachineName}' with error: '{LogHandler.FlattenException(ex)}'";
+ _logger.LogError(failureMessage);
+
+ throw new Exception(failureMessage);
}
- else return result;
}
}
}
\ No newline at end of file
diff --git a/IISU/ImplementedStoreTypes/WinSQL/ReEnrollment.cs b/IISU/ImplementedStoreTypes/WinSQL/ReEnrollment.cs
index a96782d..1154a5a 100644
--- a/IISU/ImplementedStoreTypes/WinSQL/ReEnrollment.cs
+++ b/IISU/ImplementedStoreTypes/WinSQL/ReEnrollment.cs
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// 021225 rcp 2.6.0 Cleaned up and verified code
+
using Keyfactor.Logging;
using Keyfactor.Orchestrators.Extensions;
using Keyfactor.Orchestrators.Extensions.Interfaces;
@@ -36,7 +38,7 @@ public JobResult ProcessJob(ReenrollmentJobConfiguration config, SubmitReenrollm
ClientPSCertStoreReEnrollment myReEnrollment = new ClientPSCertStoreReEnrollment(_logger, _resolver);
- // SQL ReEnrollment performs a different type of binding. Set the bindcertificate to false and call SQL Binding
+ // SQL ReEnrollment performs a different type of binding. Set the bind certificate to false and call SQL Binding
return myReEnrollment.PerformReEnrollment(config, submitReEnrollmentUpdate, CertStoreBindingTypeENUM.WinSQL);
}
}
diff --git a/IISU/ImplementedStoreTypes/WinSQL/SQLServerInventory.cs b/IISU/ImplementedStoreTypes/WinSQL/SQLServerInventory.cs
deleted file mode 100644
index dc290fa..0000000
--- a/IISU/ImplementedStoreTypes/WinSQL/SQLServerInventory.cs
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2022 Keyfactor
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-using Keyfactor.Orchestrators.Common.Enums;
-using Keyfactor.Orchestrators.Extensions;
-using Microsoft.Extensions.Logging;
-using Microsoft.Management.Infrastructure;
-using Newtonsoft.Json;
-using System.Collections.Generic;
-using System.Management.Automation;
-using System.Management.Automation.Runspaces;
-
-namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.WinSql
-{
- internal class SQLServerInventory : ClientPSCertStoreInventory
- {
- private string SqlInstanceName { get; set; }
-
- public SQLServerInventory(ILogger logger) : base(logger)
- {
- }
-
- public List GetInventoryItems(Runspace runSpace, InventoryJobConfiguration jobConfig)
- {
- var jobProperties = JsonConvert.DeserializeObject(jobConfig.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
- //SqlInstanceName = jobProperties.SqlInstanceName;
-
- // Get the raw certificate inventory from cert store
- List certificates = base.GetCertificatesFromStore(runSpace, jobConfig.CertificateStoreDetails.StorePath);
-
- // Contains the inventory items to be sent back to KF
- List myBoundCerts = new List();
- using (PowerShell ps2 = PowerShell.Create())
- {
- //runSpace.Open();
- ps2.Runspace = runSpace;
-
- //Get all the installed instances of Sql Server
- var funcScript = string.Format(@$"(Get-ItemProperty ""HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server"").InstalledInstances");
- ps2.AddScript(funcScript);
- //.LogTrace("funcScript added...");
- var instances = ps2.Invoke();
- ps2.Commands.Clear();
- var psSqlManager = new ClientPsSqlManager(jobConfig, runSpace);
- var commonInstances=new Dictionary();
-
- if (instances != null && instances[0] != null)
- {
- foreach (var instance in instances)
- {
- var regLocation = psSqlManager.GetSqlCertRegistryLocation(instance.ToString(), ps2);
-
- funcScript = string.Format(@$"Get-ItemPropertyValue ""{regLocation}"" -Name Certificate");
- ps2.AddScript(funcScript);
- //_logger.LogTrace("funcScript added...");
- var thumbprint = ps2.Invoke()[0].ToString();
- ps2.Commands.Clear();
- if (string.IsNullOrEmpty(thumbprint)) continue;
- thumbprint = thumbprint.ToUpper();
-
- if (!commonInstances.ContainsKey(thumbprint))
- {
- commonInstances.Add(thumbprint, instance.ToString());
- }
- else
- {
- commonInstances[thumbprint] = commonInstances[thumbprint] + "," + instance.ToString();
- }
- }
-
- foreach (var kp in commonInstances)
- {
- Certificate foundCert = certificates.Find(m => m.Thumbprint.ToUpper().Equals(kp.Key));
-
- if (foundCert == null) continue;
-
- var sqlSettingsDict = new Dictionary
- {
- { "InstanceName", kp.Value.ToString() },
- { "ProviderName", foundCert.CryptoServiceProvider }
- };
-
- myBoundCerts.Add(
- new CurrentInventoryItem
- {
- Certificates = new[] { foundCert.CertificateData },
- Alias = kp.Key,
- PrivateKeyEntry = foundCert.HasPrivateKey,
- UseChainLevel = false,
- ItemStatus = OrchestratorInventoryItemStatus.Unknown,
- Parameters = sqlSettingsDict
- });
- }
- return myBoundCerts;
- }
- else
- {
- return null;
- }
- }
-
- }
- }
-}
diff --git a/IISU/ImplementedStoreTypes/WinSQL/WinSQLCertificateInfo.cs b/IISU/ImplementedStoreTypes/WinSQL/WinSQLCertificateInfo.cs
new file mode 100644
index 0000000..c9c0588
--- /dev/null
+++ b/IISU/ImplementedStoreTypes/WinSQL/WinSQLCertificateInfo.cs
@@ -0,0 +1,37 @@
+// Copyright 2022 Keyfactor
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// 021225 rcp 2.6.0 Cleaned up and verified code
+
+// Ignore Spelling: Keyfactor sql
+
+namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.WinSql
+{
+
+ public class Parameters
+ {
+ public string InstanceName { get; set; }
+ public object ProviderName { get; set; }
+ }
+
+ public class WinSQLCertificateInfo
+ {
+ public string Certificates { get; set; }
+ public string Alias { get; set; }
+ public bool PrivateKeyEntry { get; set; }
+ public bool UseChainLevel { get; set; }
+ public string ItemStatus { get; set; }
+ public Parameters Parameters { get; set; }
+ }
+}
diff --git a/IISU/ImplementedStoreTypes/WinSQL/WinSqlBinding.cs b/IISU/ImplementedStoreTypes/WinSQL/WinSqlBinding.cs
new file mode 100644
index 0000000..2a0be33
--- /dev/null
+++ b/IISU/ImplementedStoreTypes/WinSQL/WinSqlBinding.cs
@@ -0,0 +1,117 @@
+// Copyright 2022 Keyfactor
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// 021225 rcp 2.6.0 Cleaned up and verified code
+
+// Ignore Spelling: Keyfactor Sql
+
+using Keyfactor.Logging;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Management.Automation;
+
+namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.WinSql
+{
+ public class WinSqlBinding
+ {
+ private static ILogger _logger;
+ private static Collection? _results = null;
+
+ public WinSqlBinding()
+ {
+ _logger = LogHandler.GetClassLogger();
+ _logger.MethodEntry();
+ }
+
+ public static bool BindSQLCertificate(PSHelper psHelper, string SQLInstanceNames, string newThumbprint, string renewalThumbprint, string storePath, bool restartSQLService)
+ {
+ _logger = LogHandler.GetClassLogger();
+ _logger.MethodEntry();
+
+ try
+ {
+ var parameters = new Dictionary
+ {
+ { "SQLInstance", SQLInstanceNames },
+ { "RenewalThumbprint", renewalThumbprint.ToLower() },
+ { "NewThumbprint", newThumbprint.ToLower() }
+ };
+
+ if (restartSQLService)
+ {
+ parameters["RestartService"] = restartSQLService;
+ }
+
+ _results = psHelper.ExecutePowerShell("Bind-KFSqlCertificate", parameters);
+ if (_results != null && _results.Count > 0)
+ {
+ // Extract value from PSObject and convert to bool
+ if (bool.TryParse(_results[0]?.BaseObject?.ToString(), out bool result))
+ {
+ _logger.LogTrace($"PowerShell function Bind-KFSqlCertificate returned: {result}");
+ return result;
+ }
+ }
+
+ _logger.LogWarning("PowerShell function Bind-KFSqlCertificate did not return a valid boolean result.");
+ return false;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error executing PowerShell function: Bind-KFSqlCertificate");
+ return false;
+ }
+ }
+
+ public static bool UnBindSQLCertificate(PSHelper psHelper, string SQLInstanceNames, bool restartSQLService)
+ {
+ _logger = LogHandler.GetClassLogger();
+ _logger.MethodEntry();
+
+ _logger.LogTrace("Entered method UnBindSQLCertificate");
+ try
+ {
+ var parameters = new Dictionary
+ {
+ { "SQLInstanceNames", SQLInstanceNames.Trim() } // Send full list at once
+ };
+
+ if (restartSQLService)
+ {
+ parameters["RestartService"] = restartSQLService;
+ }
+
+ _results = psHelper.ExecutePowerShell("Unbind-KFSqlCertificate", parameters);
+ if (_results != null && _results.Count > 0)
+ {
+ if (bool.TryParse(_results[0]?.BaseObject?.ToString(), out bool result))
+ {
+ _logger.LogTrace($"PowerShell function Unbind-KFSqlCertificate returned: {result}");
+ return result;
+ }
+ }
+
+ _logger.LogWarning("PowerShell function Unbind-KFSqlCertificate did not return a valid boolean result.");
+ return false;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error occurred while unbinding certificate(s) from SQL instance(s)");
+ return false;
+ }
+ }
+ }
+}
diff --git a/IISU/JobConfigurationParser.cs b/IISU/JobConfigurationParser.cs
index 4d64749..91bfb98 100644
--- a/IISU/JobConfigurationParser.cs
+++ b/IISU/JobConfigurationParser.cs
@@ -12,17 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// 021225 rcp 2.6.0 Cleaned up and verified code
+
using Keyfactor.Orchestrators.Extensions;
-using Microsoft.PowerShell.Commands;
using Newtonsoft.Json;
using System;
-using System.Collections.Generic;
-using System.Configuration.Internal;
-using System.Diagnostics.Contracts;
-using System.Linq;
-using System.Management.Automation.Remoting;
-using System.Net;
-using System.Text;
namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore
{
diff --git a/IISU/Models/CertificateInfo.cs b/IISU/Models/CertificateInfo.cs
new file mode 100644
index 0000000..a3e2351
--- /dev/null
+++ b/IISU/Models/CertificateInfo.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.Models
+{
+ internal class CertificateInfo
+ {
+ public string StoreName { get; set; }
+ public string Certificate { get; set; }
+ public DateTime ExpiryDate { get; set; }
+ public string Issuer { get; set; }
+ public string Thumbprint { get; set; }
+ public bool HasPrivateKey { get; set; }
+ public string SAN { get; set; }
+ public string ProviderName { get; set; }
+ public string Base64Data { get; set; }
+ }
+}
diff --git a/IISU/PAMUtilities.cs b/IISU/PAMUtilities.cs
index 38de2f3..3c5bd87 100644
--- a/IISU/PAMUtilities.cs
+++ b/IISU/PAMUtilities.cs
@@ -14,9 +14,6 @@
using Keyfactor.Orchestrators.Extensions.Interfaces;
using Microsoft.Extensions.Logging;
-using System;
-using System.Collections.Generic;
-using System.Text;
namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore
{
@@ -24,12 +21,12 @@ internal class PAMUtilities
{
internal static string ResolvePAMField(IPAMSecretResolver resolver, ILogger logger, string name, string key)
{
+ logger.LogDebug($"Attempting to resolve PAM eligible field {name}");
if (resolver == null) return key;
else
{
- return resolver.Resolve(key);
+ return string.IsNullOrEmpty(key) ? key : resolver.Resolve(key);
}
-
}
}
}
diff --git a/IISU/PSHelper.cs b/IISU/PSHelper.cs
index e1016f6..09e2341 100644
--- a/IISU/PSHelper.cs
+++ b/IISU/PSHelper.cs
@@ -1,4 +1,4 @@
-// Copyright 2022 Keyfactor
+// Copyright 2025 Keyfactor
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,123 +12,597 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// 021225 rcp 2.6.0 Cleaned up and verified code
+
using Keyfactor.Logging;
using Microsoft.Extensions.Logging;
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
using System.Management.Automation;
+using System.Management.Automation.Remoting;
using System.Management.Automation.Runspaces;
using System.Net;
+using System.Runtime.InteropServices;
+using System.Security.AccessControl;
+using System.Threading;
namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore
{
- public class PsHelper
+ public class PSHelper : IDisposable
{
private static ILogger _logger;
- public static Runspace GetClientPsRunspace(string winRmProtocol, string clientMachineName, string winRmPort, bool includePortInSpn, string serverUserName, string serverPassword)
+ private PowerShell PS;
+ private Collection _PSSession = new Collection();
+
+ private string scriptFileLocation = string.Empty;
+ private string tempKeyFilePath;
+
+ private string protocol;
+ private string port;
+ private bool useSPN;
+ private string machineName;
+ private string? argument;
+
+ private string serverUserName;
+ private string serverPassword;
+
+ private bool isLocalMachine;
+ public bool IsLocalMachine
+ {
+ get { return isLocalMachine; }
+ private set { isLocalMachine = value; }
+ }
+
+ private string clientMachineName;
+ public string ClientMachineName
+ {
+ get { return clientMachineName; }
+ private set
+ {
+ clientMachineName = value;
+
+ // Break the clientMachineName into parts
+ string[] parts = clientMachineName.Split('|');
+
+ // Extract the client machine name and arguments based upon the number of parts
+ machineName = parts.Length > 1 ? parts[0] : clientMachineName;
+ argument = parts.Length > 1 ? parts[1] : null;
+
+ // Determine if this is truly a local connection
+ isLocalMachine = (machineName != null && (machineName.ToLower() == "localhost" || machineName.ToLower() == "localmachine")) ||
+ (argument != null && argument.ToLower() == "localmachine");
+ clientMachineName = isLocalMachine ? argument ?? machineName : machineName;
+ }
+ }
+
+ public PSHelper(string protocol, string port, bool useSPN, string clientMachineName, string serverUserName, string serverPassword)
{
- _logger = LogHandler.GetClassLogger();
- _logger.MethodEntry();
+ this.protocol = protocol.ToLower();
+ this.port = port;
+ this.useSPN = useSPN;
+ ClientMachineName = clientMachineName;
+ this.serverUserName = serverUserName;
+ this.serverPassword = serverPassword;
- // 2.4 - Client Machine Name now follows the naming conventions of {clientMachineName}|{localMachine}
- // If the clientMachineName is just 'localhost', it will maintain that as locally only (as previously)
- // If there is no 2nd part to the clientMachineName, a remote PowerShell session will be created
+ _logger = LogHandler.GetClassLogger();
+ _logger.LogTrace("Entered PSHelper Constructor");
+ _logger.LogTrace($"Protocol: {this.protocol}");
+ _logger.LogTrace($"Port: {this.port}");
+ _logger.LogTrace($"UseSPN: {this.useSPN}");
+ _logger.LogTrace($"ClientMachineName: {ClientMachineName}");
+ _logger.LogTrace("Constructor Completed");
+ }
- // Break the clientMachineName into parts
- string[] parts = clientMachineName.Split('|');
+ public void Initialize()
+ {
+ _logger.LogTrace("Entered PSHelper.Initialize()");
+
+ PS = PowerShell.Create();
+
+ // Add listeners to raise events
+ PS.Streams.Debug.DataAdded += PSHelper.ProcessPowerShellScriptEvent;
+ PS.Streams.Error.DataAdded += PSHelper.ProcessPowerShellScriptEvent;
+ PS.Streams.Information.DataAdded += PSHelper.ProcessPowerShellScriptEvent;
+ PS.Streams.Verbose.DataAdded += PSHelper.ProcessPowerShellScriptEvent;
+ PS.Streams.Warning.DataAdded += PSHelper.ProcessPowerShellScriptEvent;
- // Extract the client machine name and arguments based upon the number of parts
- string machineName = parts.Length > 1 ? parts[0] : clientMachineName;
- string argument = parts.Length > 1 ? parts[1] : null;
+ _logger.LogDebug($"isLocalMachine flag set to: {isLocalMachine}");
+ _logger.LogDebug($"Protocol is set to: {protocol}");
- // Determine if this is truly a local connection
- bool isLocal = (machineName.ToLower() == "localhost") || (argument != null && argument.ToLower() == "localmachine");
+ scriptFileLocation = FindPSLocation(AppDomain.CurrentDomain.BaseDirectory, "WinCertScripts.ps1");
+ if (scriptFileLocation == null) { throw new Exception("Unable to find the accompanying PowerShell Script file: WinCertScripts.ps1"); }
- _logger.LogInformation($"Full clientMachineName={clientMachineName} | machineName={machineName} | argument={argument} | isLocal={isLocal}");
+ _logger.LogTrace($"Script file located here: {scriptFileLocation}");
- if (isLocal)
+ if (!isLocalMachine)
{
-#if NET6_0
- PowerShellProcessInstance instance = new PowerShellProcessInstance(new Version(5, 1), null, null, false);
- Runspace rs = RunspaceFactory.CreateOutOfProcessRunspace(new TypeTable(Array.Empty()), instance);
- return rs;
-#elif NET8_0_OR_GREATER
- try
- {
- InitialSessionState iss = InitialSessionState.CreateDefault();
- Runspace rs = RunspaceFactory.CreateRunspace(iss);
- return rs;
- }
- catch (global::System.Exception)
- {
- throw new Exception($"An error occurred while attempting to create the PowerShell instance. This version requires .Net8 and PowerShell SDK 7.2 or greater. Please verify the version of .Net8 and PowerShell installed on your machine.");
- }
-#endif
+ InitializeRemoteSession();
}
else
{
- var connInfo = new WSManConnectionInfo(new Uri($"{winRmProtocol}://{clientMachineName}:{winRmPort}/wsman"));
- connInfo.IncludePortInSPN = includePortInSpn;
+ InitializeLocalSession();
+ }
- _logger.LogTrace($"Creating remote session at: {connInfo.ConnectionUri}");
+ // Display Hosting information
+ string psInfo = @"
+ $psVersion = $PSVersionTable.PSVersion
+ $os = [System.Environment]::OSVersion
+ $hostName = [System.Net.Dns]::GetHostName()
- if (!string.IsNullOrEmpty(serverUserName))
+ [PSCustomObject]@{
+ PowerShellVersion = $psVersion
+ OperatingSystem = $os
+ HostName = $hostName
+ } | ConvertTo-Json
+ ";
+ var results = ExecutePowerShell(psInfo,isScript:true);
+ foreach (var result in results)
+ {
+ _logger.LogTrace($"{result}");
+ }
+ }
+
+ private void InitializeRemoteSession()
+ {
+ if (protocol == "ssh")
+ {
+ _logger.LogTrace("Initializing SSH connection");
+
+ try
{
- _logger.LogTrace($"Credentials Specified");
- var pw = new NetworkCredential(serverUserName, serverPassword).SecurePassword;
- connInfo.Credential = new PSCredential(serverUserName, pw);
+ _logger.LogInformation("Attempting to create a temporary key file");
+ tempKeyFilePath = createPrivateKeyFile();
}
- return RunspaceFactory.CreateRunspace(connInfo);
+ catch (Exception ex)
+ {
+ _logger.LogError($"Error while creating temporary KeyFilePath: {ex.Message}");
+ throw new Exception("Error while creating temporary KeyFilePath.");
+ }
+
+ Hashtable options = new Hashtable
+ {
+ { "StrictHostKeyChecking", "No" },
+ { "UserKnownHostsFile", "/dev/null" },
+ };
+
+ PS.AddCommand("New-PSSession")
+ .AddParameter("HostName", ClientMachineName)
+ .AddParameter("UserName", serverUserName)
+ .AddParameter("KeyFilePath", tempKeyFilePath)
+ .AddParameter("ConnectingTimeout", 10000)
+ .AddParameter("Options", options);
}
+ else
+ {
+ _logger.LogTrace("Initializing WinRM connection");
+ var pw = new NetworkCredential(serverUserName, serverPassword).SecurePassword;
+ PSCredential myCreds = new PSCredential(serverUserName, pw);
+
+ // Create the PSSessionOption object
+ var sessionOption = new PSSessionOption
+ {
+ IncludePortInSPN = useSPN
+ };
+
+ PS.AddCommand("New-PSSession")
+ .AddParameter("ComputerName", ClientMachineName)
+ .AddParameter("Port", port)
+ .AddParameter("Credential", myCreds)
+ .AddParameter("SessionOption", sessionOption);
+ }
+
+ using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
+
+ _logger.LogTrace("Attempting to invoke PS-Session command on remote machine.");
+ _PSSession = PS.Invoke();
+
+ if (_PSSession.Count > 0)
+ {
+ _logger.LogTrace("Session Invoked...Checking for errors.");
+ PS.Commands.Clear();
+ _logger.LogTrace("PS-Session established");
+
+ PS.AddCommand("Invoke-Command")
+ .AddParameter("Session", _PSSession)
+ .AddParameter("ScriptBlock", ScriptBlock.Create(PSHelper.LoadScript(scriptFileLocation)));
+
+ var results = PS.Invoke();
+ CheckErrors();
+ _logger.LogTrace("Script loaded into remote session successfully.");
+ }
+ else
+ {
+ throw new Exception("Failed to create the remote PowerShell Session.");
+ }
+
}
- public static IEnumerable GetCSPList(Runspace myRunspace)
+ private void InitializeLocalSession()
{
- _logger.LogTrace("Getting the list of Crypto Service Providers");
+ _logger.LogTrace("Setting Execution Policy to Unrestricted");
+ PS.AddScript("Set-ExecutionPolicy Unrestricted -Scope Process -Force");
+ PS.Invoke(); // Ensure the script is invoked and loaded
+ CheckErrors();
- using var ps = PowerShell.Create();
+ PS.Commands.Clear(); // Clear commands after loading functions
- ps.Runspace = myRunspace;
+ // Trying this to get IISAdministration loaded!!
+ PowerShellProcessInstance psInstance = new PowerShellProcessInstance(new Version(5, 1), null, null, false);
+ Runspace rs = RunspaceFactory.CreateOutOfProcessRunspace(new TypeTable(Array.Empty()), psInstance);
+ rs.Open();
- var certStoreScript = $@"
- $certUtilOutput = certutil -csplist
+ PS.Runspace = rs;
- $cspInfoList = @()
- foreach ($line in $certUtilOutput) {{
- if ($line -match ""Provider Name:"") {{
- $cspName = ($line -split "":"")[1].Trim()
- $cspInfoList += $cspName
- }}
- }}
+ _logger.LogTrace("Setting script file into memory");
+ PS.AddScript(". '" + scriptFileLocation + "'");
+ PS.Invoke(); // Ensure the script is invoked and loaded
+ CheckErrors();
- $cspInfoList";
+ PS.Commands.Clear(); // Clear commands after loading functions
+ }
- ps.AddScript(certStoreScript);
+ public void Terminate()
+ {
+ PS.Commands.Clear();
+ if (PS != null)
+ {
+ try
+ {
+ PS.AddCommand("Remove-PSSession").AddParameter("Session", _PSSession);
+ PS.Invoke();
+ CheckErrors();
+ }
+ catch (Exception)
+ {
+ }
+ }
+
+ if (File.Exists(tempKeyFilePath))
+ {
+ try
+ {
+ File.Delete(tempKeyFilePath);
+ _logger.LogTrace($"Temporary KeyFilePath deleted: {tempKeyFilePath}");
+ }
+ catch (Exception)
+ {
+ _logger.LogError($"Error while deleting KeyFilePath.");
+ }
+ }
- foreach (var result in ps.Invoke())
+ try
+ {
+ PS.Runspace.Close();
+ }
+ catch (Exception)
{
- var cspName = result?.BaseObject?.ToString();
- if (cspName != null) { yield return cspName; }
}
- _logger.LogInformation("No Crypto Service Providers were found");
- yield return null;
+ PS.Dispose();
}
- public static bool IsCSPFound(IEnumerable cspList, string userCSP)
+ public Collection? InvokeFunction(string functionName, Dictionary? parameters = null)
{
- foreach (var csp in cspList)
+ PS.Commands.Clear();
+
+ // Prepare the command
+ PS.AddCommand("Invoke-Command")
+ .AddParameter("ScriptBlock", ScriptBlock.Create(functionName));
+
+ if (!isLocalMachine)
{
- if (string.Equals(csp, userCSP, StringComparison.OrdinalIgnoreCase))
+ PS.AddParameter("Session", _PSSession);
+ }
+
+ // Add parameters
+ if (parameters != null)
+ {
+ PS.AddParameter("ArgumentList", parameters.Values.ToArray());
+ }
+
+ _logger.LogTrace($"Attempting to InvokeFunction: {functionName}");
+ var results = PS.Invoke();
+
+ if (PS.HadErrors)
+ {
+ string errorMessages = string.Join("; ", PS.Streams.Error.Select(e => e.ToString()));
+ throw new Exception($"Error executing function '{functionName}': {errorMessages}");
+ }
+
+ return results;
+ }
+
+ public Collection ExecutePowerShellScript(string script)
+ {
+ PS.AddScript(script);
+ return PS.Invoke();
+ }
+
+
+ public Collection? ExecutePowerShell(string commandOrScript, Dictionary? parameters = null, bool isScript = false)
+ {
+ try
+ {
+ PS.Commands.Clear();
+
+ // Handle Local or Remote Execution
+ if (isLocalMachine)
+ {
+ if (isScript)
+ {
+ // Add script content directly for local execution
+ PS.AddScript(commandOrScript);
+ }
+ else
+ {
+ // Add command for local execution
+ PS.AddCommand(commandOrScript);
+ }
+ }
+ else
+ {
+ // For remote execution, use Invoke-Command
+ var scriptBlock = isScript
+ ? ScriptBlock.Create(commandOrScript) // Use the script as a ScriptBlock
+ : ScriptBlock.Create($"& {{ {commandOrScript} }}"); // Wrap commands in ScriptBlock
+
+ PS.AddCommand("Invoke-Command")
+ .AddParameter("Session", _PSSession)
+ .AddParameter("ScriptBlock", scriptBlock);
+ }
+
+ // Add Parameters if provided
+ if (parameters != null)
+ {
+ if (isLocalMachine || isScript)
+ {
+ foreach (var param in parameters)
+ {
+ PS.AddParameter(param.Key, param.Value);
+ }
+ }
+ else
+ {
+ // Remote execution: Use ArgumentList for parameters
+ var paramBlock = string.Join(", ", parameters.Select(p => $"[{p.Value.GetType().Name}] ${p.Key}"));
+ var paramUsage = string.Join(" ", parameters.Select(p => $"-{p.Key} ${p.Key}"));
+
+ string scriptBlockWithParams = $@"
+ param({paramBlock})
+ {commandOrScript} {paramUsage}
+ ";
+
+ PS.Commands.Clear(); // Clear previous commands
+ PS.AddCommand("Invoke-Command")
+ .AddParameter("Session", _PSSession)
+ .AddParameter("ScriptBlock", ScriptBlock.Create(scriptBlockWithParams))
+ .AddParameter("ArgumentList", parameters.Values.ToArray());
+ }
+ }
+
+ // Log and execute
+ _logger.LogTrace($"Executing PowerShell: {commandOrScript}");
+ var results = PS.Invoke();
+
+ // Check for errors
+ if (PS.HadErrors)
+ {
+ string errorMessages = string.Join("; ", PS.Streams.Error.Select(e => e.ToString()));
+ throw new Exception($"PowerShell execution errors: {errorMessages}");
+ }
+
+ return results;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError($"Error executing PowerShell: {ex.Message}");
+ throw;
+ }
+ finally
+ {
+ PS.Commands.Clear();
+ }
+ }
+
+ private void CheckErrors()
+ {
+ _logger.LogTrace("Checking PowerShell session for errors.");
+
+ string errorList = string.Empty;
+ if (PS.HadErrors)
+ {
+ errorList = string.Empty;
+ foreach (var error in PS.Streams.Error)
+ {
+ errorList += error + "\n";
+ _logger.LogError($"Error: {error}");
+ }
+
+ throw new Exception(errorList);
+ }
+ }
+
+ public static string LoadScript(string scriptFileName)
+ {
+ string scriptFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PowerShellScripts", scriptFileName);
+ _logger.LogTrace($"Attempting to load script {scriptFilePath}");
+
+ if (File.Exists(scriptFilePath))
+ {
+ return File.ReadAllText(scriptFilePath);
+ }else
+ { throw new Exception($"File: {scriptFilePath} was not found."); }
+ }
+
+ private static string FindPSLocation(string directory, string fileName)
+ {
+ try
+ {
+ foreach (string file in Directory.GetFiles(directory))
+ {
+ if (Path.GetFileName(file).Equals(fileName, StringComparison.OrdinalIgnoreCase))
+ {
+ return Path.GetFullPath(file);
+ }
+ }
+
+ foreach (string subDir in Directory.GetDirectories(directory))
+ {
+ string result = FindPSLocation(subDir, fileName);
+ if (!string.IsNullOrEmpty(result))
+ {
+ return result;
+ }
+ }
+ }
+ catch (UnauthorizedAccessException)
+ {
+ }
+
+ return null;
+ }
+
+ public static void ProcessPowerShellScriptEvent(object? sender, DataAddedEventArgs e)
+ {
+ if (sender != null)
+ {
+ {
+ switch (sender)
+ {
+ case PSDataCollection:
+ var debugMessages = sender as PSDataCollection;
+ if (debugMessages != null)
+ {
+ var debugMessage = debugMessages[e.Index];
+ _logger.LogDebug($"Debug: {debugMessage.Message}");
+ }
+ break;
+ case PSDataCollection:
+ var verboseMessages = sender as PSDataCollection;
+ if (verboseMessages != null)
+ {
+ var verboseMessage = verboseMessages[e.Index];
+ _logger.LogTrace($"Verbose: {verboseMessage.Message}");
+ }
+ break;
+ case PSDataCollection:
+ var errorMessages = sender as PSDataCollection;
+ if (errorMessages != null)
+ {
+ var errorMessage = errorMessages[e.Index];
+ _logger.LogError($"Error: {errorMessage.Exception.Message}");
+ }
+ break;
+ case PSDataCollection:
+ var infoMessages = sender as PSDataCollection;
+ if (infoMessages != null)
+ {
+ var infoMessage = infoMessages[e.Index];
+ _logger.LogInformation($"INFO: {infoMessage.MessageData.ToString()}");
+ }
+ break;
+
+ case PSDataCollection:
+ var warningMessages = sender as PSDataCollection;
+ if (warningMessages != null)
+ {
+ var warningMessage = warningMessages[e.Index];
+ _logger.LogWarning($"WARN: {warningMessage.Message}");
+ }
+ break;
+ default:
+ break;
+
+ }
+ }
+ }
+ }
+
+ public void Dispose()
+ {
+ if (PS != null)
+ {
+ // Remove the listeners
+ PS.Streams.Debug.DataAdded -= PSHelper.ProcessPowerShellScriptEvent;
+ PS.Streams.Error.DataAdded -= PSHelper.ProcessPowerShellScriptEvent;
+ PS.Streams.Information.DataAdded -= PSHelper.ProcessPowerShellScriptEvent;
+ PS.Streams.Verbose.DataAdded -= PSHelper.ProcessPowerShellScriptEvent;
+ PS.Streams.Warning.DataAdded -= PSHelper.ProcessPowerShellScriptEvent;
+
+ PS.Dispose();
+ }
+ }
+
+ private string createPrivateKeyFile()
+ {
+ string tmpFile = Path.GetTempFileName(); // "logs/AdminFile";
+ _logger.LogTrace($"Created temporary KeyFilePath: {tmpFile}, writing bytes.");
+
+ File.WriteAllText(tmpFile, formatPrivateKey(serverPassword));
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ _logger.LogTrace($"Changing permissions on Windows temp file: {tmpFile}.");
+
+ // Create a FileInfo object for the file
+ FileInfo fileInfo = new FileInfo(tmpFile);
+
+ // Get the current access control settings of the file
+ FileSecurity fileSecurity = fileInfo.GetAccessControl();
+
+ // Remove existing permissions
+ fileSecurity.RemoveAccessRuleAll(new FileSystemAccessRule("Everyone", FileSystemRights.FullControl, AccessControlType.Allow));
+
+ // Grant read permissions to the current user
+ string currentUser = Environment.UserName;
+ fileSecurity.AddAccessRule(new FileSystemAccessRule(currentUser, FileSystemRights.Read, AccessControlType.Allow));
+
+ // Deny all access to others (this is optional, depending on your use case)
+ fileSecurity.AddAccessRule(new FileSystemAccessRule("Everyone", FileSystemRights.Read, AccessControlType.Deny));
+
+ // Apply the modified permissions to the file
+ fileInfo.SetAccessControl(fileSecurity);
+ //File.SetAttributes(tmpFile, FileAttributes.ReadOnly);
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ ProcessStartInfo chmodInfo = new ProcessStartInfo()
+ {
+ FileName = "/bin/chmod",
+ Arguments = "600 " + tmpFile,
+ RedirectStandardOutput = true,
+ UseShellExecute = false,
+ CreateNoWindow = true
+ };
+ using (Process chmodProcess = new Process() { StartInfo = chmodInfo })
{
- _logger.LogTrace($"CSP found: {csp}");
- return true;
+ chmodProcess.Start();
+ chmodProcess.WaitForExit();
+ if (chmodProcess.ExitCode == 0)
+ {
+ _logger.LogInformation("File permissions set to 600.");
+ }
+ else
+ {
+ _logger.LogWarning("Failed to set file permissions.");
+ }
}
}
- _logger.LogTrace($"CSP: {userCSP} was not found");
- return false;
+
+ return tmpFile;
+ }
+
+ private static string formatPrivateKey(string privateKey)
+ {
+ String keyType = privateKey.Contains("OPENSSH PRIVATE KEY") ? "OPENSSH" : "RSA";
+
+ return privateKey.Replace($" {keyType} PRIVATE ", "^^^").Replace(" ", System.Environment.NewLine).Replace("^^^", $" {keyType} PRIVATE ") + System.Environment.NewLine;
}
}
}
diff --git a/IISU/PowerShellScripts/WinCertScripts.ps1 b/IISU/PowerShellScripts/WinCertScripts.ps1
new file mode 100644
index 0000000..0bed17f
--- /dev/null
+++ b/IISU/PowerShellScripts/WinCertScripts.ps1
@@ -0,0 +1,1032 @@
+# Set preferences globally at the script level
+$DebugPreference = "Continue"
+$VerbosePreference = "Continue"
+$InformationPreference = "Continue"
+
+function Get-KFCertificates {
+ param (
+ [string]$StoreName = "My" # Default store name is "My" (Personal)
+ )
+
+ # Define the store path using the provided StoreName parameter
+ $storePath = "Cert:\LocalMachine\$StoreName"
+
+ # Check if the store path exists to ensure the store is valid
+ if (-not (Test-Path $storePath)) {
+ # Write an error message and exit the function if the store path is invalid
+ Write-Error "The certificate store path '$storePath' does not exist. Please provide a valid store name."
+ return
+ }
+
+ # Retrieve all certificates from the specified store
+ $certificates = Get-ChildItem -Path $storePath
+
+ # Initialize an empty array to store certificate information objects
+ $certInfoList = @()
+
+ foreach ($cert in $certificates) {
+ try {
+ # Create a custom object to store details about the current certificate
+ $certInfo = [PSCustomObject]@{
+ StoreName = $StoreName # Name of the certificate store
+ Certificate = $cert.Subject # Subject of the certificate
+ ExpiryDate = $cert.NotAfter # Expiration date of the certificate
+ Issuer = $cert.Issuer # Issuer of the certificate
+ Thumbprint = $cert.Thumbprint # Unique thumbprint of the certificate
+ HasPrivateKey = $cert.HasPrivateKey # Indicates if the certificate has a private key
+ SAN = Get-KFSAN $cert # Subject Alternative Names (if available)
+ ProviderName = Get-CertificateCSP $cert # Provider of the certificate
+ Base64Data = [System.Convert]::ToBase64String($cert.RawData) # Encoded raw certificate data
+ }
+
+ # Add the certificate information object to the results array
+ $certInfoList += $certInfo
+ } catch {
+ # Write a warning message if there is an error processing the current certificate
+ Write-Warning "An error occurred while processing the certificate: $_"
+ }
+ }
+
+ # Output the results in JSON format if certificates were found
+ if ($certInfoList) {
+ $certInfoList | ConvertTo-Json -Depth 10
+ } else {
+ # Write a warning if no certificates were found in the specified store
+ Write-Warning "No certificates were found in the store '$StoreName'."
+ }
+}
+
+function Get-KFIISBoundCertificates {
+ # Import the IISAdministration module
+ Import-Module IISAdministration
+
+ # Get all websites
+ $websites = Get-IISSite
+
+ # Write the count of websites found
+ Write-Information "There were $($websites.Count) websites found."
+
+ # Initialize an array to store the results
+ $certificates = @()
+
+ # Initialize a counter for the total number of bindings with certificates
+ $totalBoundCertificates = 0
+
+ foreach ($site in $websites) {
+ # Get the site name
+ $siteName = $site.name
+
+ # Get the bindings for the site
+ $bindings = Get-IISSiteBinding -Name $siteName
+
+ # Initialize a counter for bindings with certificates for the current site
+ $siteBoundCertificateCount = 0
+
+ foreach ($binding in $bindings) {
+ # Check if the binding has an SSL certificate
+ if ($binding.protocol -eq 'https' -and $binding.RawAttributes.certificateHash) {
+ # Get the certificate hash
+ $certHash = $binding.RawAttributes.certificateHash
+
+ # Get the certificate store
+ $StoreName = $binding.certificateStoreName
+
+ try {
+ # Get the certificate details from the certificate store
+ $cert = Get-ChildItem -Path "Cert:\LocalMachine\$StoreName\$certHash"
+
+ # Convert certificate data to Base64
+ $certBase64 = [Convert]::ToBase64String($cert.RawData)
+
+ # Create a custom object to store the results
+ $certInfo = [PSCustomObject]@{
+ SiteName = $siteName
+ Binding = $binding.bindingInformation
+ IPAddress = ($binding.bindingInformation -split ":")[0]
+ Port = ($binding.bindingInformation -split ":")[1]
+ Hostname = ($binding.bindingInformation -split ":")[2]
+ Protocol = $binding.protocol
+ SNI = $binding.sslFlags -eq 1
+ ProviderName = Get-CertificateCSP $cert
+ SAN = Get-KFSAN $cert
+ Certificate = $cert.Subject
+ ExpiryDate = $cert.NotAfter
+ Issuer = $cert.Issuer
+ Thumbprint = $cert.Thumbprint
+ HasPrivateKey = $cert.HasPrivateKey
+ CertificateBase64 = $certBase64
+ }
+
+ # Add the certificate information to the array
+ $certificates += $certInfo
+
+ # Increment the counters
+ $siteBoundCertificateCount++
+ $totalBoundCertificates++
+ } catch {
+ Write-Warning "Could not retrieve certificate details for hash $certHash in store $StoreName."
+ }
+ }
+ }
+
+ # Write the count of bindings with certificates for the current site
+ Write-Information "Website: $siteName has $siteBoundCertificateCount bindings with certificates."
+ }
+
+ # Write the total count of bindings with certificates
+ Write-Information "A total of $totalBoundCertificates bindings with valid certificates were found."
+
+ # Output the results in JSON format or indicate no certificates found
+ if ($totalBoundCertificates -gt 0) {
+ $certificates | ConvertTo-Json
+ } else {
+ Write-Information "No valid certificates were found bound to websites."
+ }
+}
+
+function Add-KFCertificateToStore{
+ param (
+ [Parameter(Mandatory = $true)]
+ [string]$Base64Cert,
+
+ [Parameter(Mandatory = $false)]
+ [string]$PrivateKeyPassword,
+
+ [Parameter(Mandatory = $true)]
+ [string]$StoreName,
+
+ [Parameter(Mandatory = $false)]
+ [string]$CryptoServiceProvider
+ )
+
+ try {
+
+ Write-Information "Base64Cert: $Base64Cert"
+ Write-Information "PrivateKeyPassword: $PrivateKeyPassword"
+ Write-Information "StoreName: $StoreName"
+ Write-Information "CryptoServiceProvider: $CryptoServiceProvider"
+
+ $thumbprint = $null
+
+ if ($CryptoServiceProvider)
+ {
+ # Test to see if CSP exists
+ if(-not (Test-CryptoServiceProvider -CSPName $CryptoServiceProvider))
+ {
+ Write-Information "INFO: The CSP $CryptoServiceProvider was not found on the system."
+ Write-Warning "WARN: CSP $CryptoServiceProvider was not found on the system."
+ return
+ }
+
+ Write-Information "Adding certificate with the CSP '$CryptoServiceProvider'"
+
+ # Write certificate to a temporary PFX file
+ $tempFileName = [System.IO.Path]::GetTempFileName() + '.pfx'
+ [System.IO.File]::WriteAllBytes($tempFileName, [System.Convert]::FromBase64String($Base64Cert))
+
+ # Initialize output variable
+ $output = ""
+
+ # Execute certutil based on whether a private key password was supplied
+ try {
+ # Generate the appropriate certutil command based on the parameters
+ $cryptoProviderPart = if ($CryptoServiceProvider) { "-csp `"$CryptoServiceProvider`" " } else { "" }
+ $passwordPart = if ($PrivateKeyPassword) { "-p `"$PrivateKeyPassword`" " } else { "" }
+ $action = if ($PrivateKeyPassword) { "importpfx" } else { "addstore" }
+
+ # Construct the full certutil command
+ $command = "certutil -f $cryptoProviderPart$passwordPart-$action $StorePath `"$tempFileName`""
+ $output = Invoke-Expression $command
+
+ # Check for errors based on the last exit code
+ if ($LASTEXITCODE -ne 0) {
+ throw "Certutil failed with exit code $LASTEXITCODE. Output: $output"
+ }
+
+ # Additional check for known error keywords in output (optional)
+ if ($output -match "(ERROR|FAILED|EXCEPTION)") {
+ throw "Certutil output indicates an error: $output"
+ }
+
+ # Retrieve the certificate thumbprint from the store
+ $cert = Get-ChildItem -Path "Cert:\LocalMachine\$StoreName" | Sort-Object -Property NotAfter -Descending | Select-Object -First 1
+ if ($cert) {
+ $output = $cert.Thumbprint
+ Write-Output "Certificate imported successfully. Thumbprint: $thumbprint"
+ }
+ else {
+ throw "Certificate import succeeded, but no certificate was found in the $StoreName store."
+ }
+
+ } catch {
+ # Handle any errors and log the exception message
+ Write-Error "Error during certificate import: $_"
+ $output = "Error: $_"
+ } finally {
+ # Ensure the temporary file is deleted
+ if (Test-Path $tempFileName) {
+ Remove-Item $tempFileName -Force
+ }
+ }
+
+ # Output the final result
+ return $output
+
+ } else {
+ $bytes = [System.Convert]::FromBase64String($Base64Cert)
+ $certStore = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $storeName, "LocalMachine"
+ Write-Information "Store '$StoreName' is open."
+ $certStore.Open(5)
+
+ $cert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList $bytes, $PrivateKeyPassword, 18 <# Persist, Machine #>
+ $certStore.Add($cert)
+ $certStore.Close();
+ Write-Information "Store '$StoreName' is closed."
+
+ # Get the thumbprint so it can be returned to the calling function
+ $thumbprint = $cert.Thumbprint
+ Write-Information "The thumbprint '$thumbprint' was created."
+ }
+
+ Write-Host "Certificate added successfully to $StoreName."
+ return $thumbprint
+ } catch {
+ Write-Error "An error occurred: $_"
+ return $null
+ }
+}
+
+function Remove-KFCertificateFromStore {
+ param (
+ [string]$Thumbprint,
+ [string]$StorePath,
+
+ [parameter(ParameterSetName = $false)]
+ [switch]$IsAlias
+ )
+
+ # Initialize a variable to track success
+ $isSuccessful = $false
+
+ try {
+ # Open the certificate store
+ $store = New-Object System.Security.Cryptography.X509Certificates.X509Store($StorePath, [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine)
+ $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
+
+ # Find the certificate by thumbprint or alias
+ if ($IsAlias) {
+ $cert = $store.Certificates | Where-Object { $_.FriendlyName -eq $Thumbprint }
+ } else {
+ $cert = $store.Certificates | Where-Object { $_.Thumbprint -eq $Thumbprint }
+ }
+
+ if ($cert) {
+ # Remove the certificate from the store
+ Write-Information "Attempting to remove certificate from store '$StorePath' with the thumbprint: $Thumbprint"
+ $store.Remove($cert)
+ Write-Information "Certificate removed successfully from store '$StorePath'"
+
+ # Mark success
+ $isSuccessful = $true
+ } else {
+ throw [System.Exception]::new("Certificate not found in $StorePath.")
+ }
+
+ # Close the store
+ $store.Close()
+ } catch {
+ # Log and rethrow the exception
+ Write-Error "An error occurred: $_"
+ throw $_
+ } finally {
+ # Ensure the store is closed
+ if ($store) {
+ $store.Close()
+ }
+ }
+
+ # Return the success status
+ return $isSuccessful
+}
+
+function New-KFIISSiteBinding{
+ param (
+ [Parameter(Mandatory = $True)] $SiteName, # The name of the IIS site
+ [Parameter(Mandatory = $True)] $IPAddress, # The IP Address for the binding
+ [Parameter(Mandatory = $True)] $Port, # The port number for the binding
+ [Parameter(Mandatory = $False)] $Hostname, # Host name for the binding (if any)
+ [Parameter(Mandatory = $True)] $Protocol, # Protocol (e.g., HTTP, HTTPS)
+ [Parameter(Mandatory = $True)] $Thumbprint, # Certificate thumbprint for HTTPS bindings
+ [Parameter(Mandatory = $True)] $StoreName, # Certificate store location (e.g., ""My"" for personal certs)
+ [Parameter(Mandatory = $True)] $SslFlags # SSL flags (if any)
+ )
+
+ Write-Debug "Attempting to import modules WebAdministration and IISAdministration"
+ try {
+ Import-Module WebAdministration -ErrorAction Stop
+ }
+ catch {
+ throw "Failed to load the WebAdministration module. Ensure it is installed and available."
+ }
+
+ # Check if the IISAdministration module is already loaded
+ if (-not (Get-Module -Name IISAdministration )) {
+ try {
+ # Attempt to import the IISAdministration module
+ Import-Module IISAdministration -ErrorAction Stop
+ }
+ catch {
+ throw "Failed to load the IISAdministration module. This function requires IIS Develpment and SCripting tools. Please ensure these tools have been installed on the IIS Server."
+ }
+ }
+
+ Write-Debug "Finished importing required modules"
+
+ Write-Verbose "INFO: Entered New-KFIISSiteBinding."
+ Write-Verbose "SiteName: $SiteName"
+ Write-Verbose "IPAddress: $IPAddress"
+ Write-Verbose "Port: $Port"
+ Write-Verbose "HostName: $HostName"
+ Write-Verbose "Protocol: $Protocol"
+ Write-Verbose "Thumbprint: $Thumbprint"
+ Write-Verbose "Store Path: $StoreName"
+ Write-Verbose "SslFlags: $SslFlags"
+
+ # Retrieve the existing binding information
+ $myBinding = "${IPAddress}:${Port}:${Hostname}" #*:443:MyHostName1 : *:443:ManualHostName
+ Write-Verbose "Formatted binding information: $myBinding"
+
+ # Check if the binding exists (NOTE: Bindings always occur using https)
+ try {
+ Write-Verbose "Attempting to get binding information for Site: '$SiteName' with bindings: $myBinding"
+ $existingBinding = Get-IISSiteBinding -Name $SiteName -Protocol $Protocol -BindingInformation $myBinding
+ }
+ catch {
+ Write-Verbose "Error occurred while attempting to get the bindings for: '$SiteName'"
+ Write-Verbose $_
+ throw $_
+ }
+
+ if ($null -ne $existingBinding) {
+ Write-Verbose "A binding for: $myBinding was found."
+
+ $currentThumbprint = ($existingBinding.CertificateHash | ForEach-Object { $_.ToString("X2") }) -join ""
+ if ($thumbprint -eq $currentThumbprint)
+ {
+ Write-Verbose "The existing binding's thumbprint matches the new thumbprint - skipping."
+ Write-Information "The thumbprint for this binding is the same as the new one."
+ Write-Information "Since they are the same thumbprint, thins binding will be skipped."
+ return
+ }
+ Write-Verbose "Current thumbprint: $currentThumbprint"
+ Write-Verbose "Will remove the existing binding prior to replacing it with new thumbprint."
+
+ # Remove the existing binding
+ Remove-IISSiteBinding -Name $SiteName -BindingInformation $existingBinding.BindingInformation -Protocol $existingBinding.Protocol -Confirm:$false
+ Write-Verbose "Removed existing binding: $($existingBinding.BindingInformation)"
+ }else{
+ Write-Verbose "No binding was found for: $myBinding."
+ }
+
+ # Create the new binding with modified properties
+ try
+ {
+ Write-Verbose "Attempting to add IISSiteBinding"
+ New-IISSiteBinding -Name $SiteName `
+ -BindingInformation $myBinding `
+ -Protocol $Protocol `
+ -CertificateThumbprint $Thumbprint `
+ -CertStoreLocation $StoreName `
+ -SslFlag $SslFlags
+
+ Write-Verbose "New Site Binding for '$SiteName' has been created with bindings: $myBinding and thumbprint: $thumbprint."
+ }
+ catch {
+ throw $_
+ };
+
+ return $True
+}
+
+function Remove-KFIISSiteBinding{
+ param (
+ [Parameter(Mandatory=$true)] $SiteName, # The name of the IIS website
+ [Parameter(Mandatory=$true)] $IPAddress, # The IP address of the binding
+ [Parameter(Mandatory=$true)] $Port, # The port number (e.g., 443 for HTTPS)
+ [Parameter(Mandatory=$false)] $Hostname # The hostname (empty string for binding without hostname)
+ )
+
+ Write-Debug "Attempting to import modules WebAdministration and IISAdministration"
+ try {
+ Import-Module WebAdministration -ErrorAction Stop
+ }
+ catch {
+ throw "Failed to load the WebAdministration module. Ensure it is installed and available."
+ }
+
+ # Check if the IISAdministration module is already loaded
+ if (-not (Get-Module -Name IISAdministration )) {
+ try {
+ # Attempt to import the IISAdministration module
+ Import-Module IISAdministration -ErrorAction Stop
+ }
+ catch {
+ throw "Failed to load the IISAdministration module. This function requires IIS Develpment and SCripting tools. Please ensure these tools have been installed on the IIS Server."
+ }
+ }
+
+ Write-Debug "Finished importing required modules"
+
+ try {
+ # Construct the binding information string correctly
+ $bindingInfo = if ($HostName) { "$IPAddress`:$Port`:$HostName" } else { "$IPAddress`:$Port`:" }
+ Write-Verbose "Checking for existing binding: $bindingInfo"
+
+ # Get the existing binding based on the constructed binding information
+ $bindings = Get-IISSiteBinding -Name $SiteName -Protocol "https" | Where-Object { $_.BindingInformation -eq $bindingInfo }
+
+ if ($bindings) {
+ Write-Verbose "Found binding: $bindingInfo for site: '$SiteName'. Removing..."
+
+ # Remove the binding
+ Remove-IISSiteBinding -Name $SiteName -BindingInformation $bindingInfo -Protocol "https" -Confirm:$false
+
+ Write-Verbose "Successfully removed binding: $bindingInfo"
+ return $true
+ }else{
+ Write-Verbose "No binding was found for: $bindingInfo."
+ return $false
+ }
+
+ } catch {
+ Write-Error "An error occurred while attempting to remove bindings for site: $SiteName"
+ Write-Error $_
+ throw $_
+ }
+}
+
+# Function to get certificate information for a SQL Server instance
+function GET-KFSQLInventory {
+ # Retrieve all SQL Server instances
+ $sqlInstances = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server").InstalledInstances
+ Write-Information "There are $($sqlInstances.Count) instances that will be checked for certificates."
+
+ # Dictionary to store instance names by thumbprint
+ $commonInstances = @{}
+
+ # First loop: gather thumbprints for each instance
+ foreach ($instance in $sqlInstances) {
+ Write-Information "Checking instance: $instance for Certificates."
+
+ # Get the registry path for the SQL instance
+ $fullInstanceName = Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL" -Name $instance
+ $regLocation = "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$fullInstanceName\MSSQLServer\SuperSocketNetLib"
+
+ try {
+ # Retrieve the certificate thumbprint from the registry
+ $thumbprint = (Get-ItemPropertyValue -Path $regLocation -Name "Certificate" -ErrorAction Stop).ToUpper()
+
+ if ($thumbprint) {
+ # Store instance names by thumbprint
+ if ($commonInstances.ContainsKey($thumbprint)) {
+ $commonInstances[$thumbprint] += ",$instance"
+ } else {
+ $commonInstances[$thumbprint] = $instance
+ }
+ }
+ } catch {
+ Write-Information "No certificate found for instance: $instance."
+ }
+ }
+
+ # Array to store results
+ $myBoundCerts = @()
+
+ # Second loop: process each unique thumbprint and gather certificate data
+ foreach ($kp in $commonInstances.GetEnumerator()) {
+ $thumbprint = $kp.Key
+ $instanceNames = $kp.Value
+
+ # Find the certificate in the local machine store
+ $certStore = "My"
+ $cert = Get-ChildItem -Path "Cert:\LocalMachine\$certStore\$thumbprint" -ErrorAction SilentlyContinue
+
+ if ($cert) {
+ # Create a hashtable with the certificate parameters
+ $sqlSettingsDict = @{
+ InstanceName = $instanceNames
+ ProviderName = $cert.PrivateKey.CspKeyContainerInfo.ProviderName
+ }
+
+ # Build the inventory item for this certificate
+ $inventoryItem = [PSCustomObject]@{
+ Certificates = [Convert]::ToBase64String($cert.RawData)
+ Alias = $thumbprint
+ PrivateKeyEntry = $cert.HasPrivateKey
+ UseChainLevel = $false
+ ItemStatus = "Unknown" # OrchestratorInventoryItemStatus.Unknown equivalent
+ Parameters = $sqlSettingsDict
+ }
+
+ # Add the inventory item to the results array
+ $myBoundCerts += $inventoryItem
+ }
+ }
+
+ # Return the array of inventory items
+ return $myBoundCerts | ConvertTo-Json
+}
+
+function Bind-KFSqlCertificate {
+ param (
+ [string]$SQLInstance,
+ [string]$RenewalThumbprint,
+ [string]$NewThumbprint,
+ [switch]$RestartService = $false
+ )
+
+ function Get-SqlCertRegistryLocation($InstanceName) {
+ return "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$InstanceName\MSSQLServer\SuperSocketNetLib"
+ }
+
+ $bindingSuccess = $true # Track success/failure
+
+ try {
+ $SQLInstances = $SQLInstance -split ',' | ForEach-Object { $_.Trim() }
+
+ foreach ($instance in $SQLInstances) {
+ try {
+ $fullInstance = Get-ItemPropertyValue -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL" -Name $instance -ErrorAction Stop
+ $regLocation = Get-SqlCertRegistryLocation -InstanceName $fullInstance
+
+ if (-not (Test-Path $regLocation)) {
+ Write-Error "Registry location not found: $regLocation"
+ $bindingSuccess = $false
+ continue
+ }
+ Write-Verbose "Instance: $instance"
+ Write-Verbose "Full Instance: $fullInstance"
+ Write-Verbose "Registry Location: $regLocation"
+ Write-Verbose "Current Thumbprint: $currentThumbprint"
+
+ $currentThumbprint = Get-ItemPropertyValue -Path $regLocation -Name "Certificate" -ErrorAction SilentlyContinue
+
+ if ($RenewalThumbprint -and $RenewalThumbprint -contains $currentThumbprint) {
+ Write-Information "Renewal thumbprint matches for instance: $fullInstance"
+ $result = Set-KFCertificateBinding -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
+ }
+
+ if (-not $result) {
+ Write-Error "Failed to bind certificate for instance: $instance"
+ $bindingSuccess = $false
+ }
+ }
+ catch {
+ Write-Error "Error processing instance '$instance': $($_.Exception.Message)"
+ $bindingSuccess = $false
+ }
+ }
+ }
+ catch {
+ Write-Error "An unexpected error occurred: $($_.Exception.Message)"
+ return $false
+ }
+
+ return $bindingSuccess
+}
+
+function Set-KFCertificateBinding {
+ param (
+ [string]$InstanceName,
+ [string]$NewThumbprint,
+ [switch]$RestartService
+ )
+
+ Write-Information "Binding certificate with thumbprint $NewThumbprint to instance $InstanceName..."
+
+ 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
+ try {
+ Set-ItemProperty -Path $RegistryPath -Name "Certificate" -Value $NewThumbprint -ErrorAction Stop
+ Write-Information "Updated registry for instance $InstanceName with new thumbprint."
+ }
+ catch {
+ Write-Error "Failed to update registry at {$RegistryPath}: $_"
+ throw $_ # Rethrow the error to ensure it's caught at a higher level
+ }
+
+ # 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"
+ }
+
+ 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."
+ }
+
+ # 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"
+
+ 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)
+
+ Set-Acl -Path $privKeyPath.FullName -AclObject $Acl -ErrorAction Stop
+ Write-Information "Updated ACL for private key at $privKeyPath."
+ }
+ catch {
+ Write-Error "Failed to update ACL on the private key: $_"
+ throw $_
+ }
+
+ # Optionally restart the SQL Server service
+ if ($RestartService) {
+ try {
+ Write-Information "Restarting SQL Server service: $serviceName..."
+ Restart-Service -Name $serviceName -Force -ErrorAction Stop
+ Write-Information "SQL Server service restarted successfully."
+ }
+ catch {
+ Write-Error "Failed to restart SQL Server service: $_"
+ throw $_
+ }
+ }
+
+ Write-Information "Certificate binding completed for instance $InstanceName."
+ }
+ catch {
+ Write-Error "An error occurred: $_"
+ return $false
+ }
+
+ return $true
+}
+
+function Unbind-KFSqlCertificate {
+ param (
+ [string]$SQLInstanceNames, # Comma-separated list of SQL instances
+ [switch]$RestartService # Restart SQL Server after unbinding
+ )
+
+ $unBindingSuccess = $true # Track success/failure
+
+ try {
+
+ $instances = $SQLInstanceNames -split ',' | ForEach-Object { $_.Trim() }
+
+ foreach ($instance in $instances) {
+ try {
+ # Resolve full instance name from registry
+ $fullInstance = Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL" -Name $instance
+ $regPath = "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$fullInstance\MSSQLServer\SuperSocketNetLib"
+
+ # Get current thumbprint
+ $certificateThumbprint = Get-ItemPropertyValue -Path $regPath -Name "Certificate" -ErrorAction SilentlyContinue
+
+ if ($certificateThumbprint) {
+ Write-Information "Unbinding certificate from SQL Server instance: $instance (Thumbprint: $certificateThumbprint)"
+
+ # Instead of deleting, set to an empty string to prevent SQL startup issues
+ Set-ItemProperty -Path $regPath -Name "Certificate" -Value ""
+
+ Write-Information "Successfully unbound certificate from SQL Server instance: $instance."
+ } else {
+ Write-Warning "No certificate bound to SQL Server instance: $instance."
+ }
+
+ # Restart service if required
+ if ($RestartService) {
+ $serviceName = Get-SqlServiceName -InstanceName $instance
+ Write-Information "Restarting SQL Server service: $serviceName..."
+ Restart-Service -Name $serviceName -Force
+ Write-Information "SQL Server service restarted successfully."
+ }
+ }
+ catch {
+ Write-Error "Failed to unbind certificate from instance: $instance. Error: $_"
+ $unBindingSuccess = $false
+ }
+ }
+ }
+ catch {
+ Write-Error "An unexpected error occurred: $($_.Exception.Message)"
+ return $false
+ }
+
+ return $unBindingSuccess
+}
+
+# Example usage:
+# Bind-CertificateToSqlInstance -Thumbprint "123ABC456DEF" -SqlInstanceName "MSSQLSERVER"
+
+function Get-SqlServiceName {
+ param (
+ [string]$InstanceName
+ )
+ if ($InstanceName -eq "MSSQLSERVER") {
+ return "MSSQLSERVER" # Default instance
+ } else {
+ return "MSSQL`$$InstanceName" # Named instance (escape $ for PowerShell strings)
+ }
+}
+
+function Get-SQLServiceUser {
+ param (
+ [Parameter(Mandatory = $true)]
+ [string]$SQLServiceName
+ )
+
+ # Use Get-CimInstance instead of Get-WmiObject
+ $serviceUser = (Get-CimInstance -ClassName Win32_Service -Filter "Name='$SQLServiceName'").StartName
+
+ if ($serviceUser) {
+ return $serviceUser
+ } else {
+ Write-Error "SQL Server instance '$SQLInstanceName' not found or no service user associated."
+ return $null
+ }
+}
+
+# Example usage:
+# Get-SQLServiceUser -SQLInstanceName "MSSQLSERVER"
+
+##### ReEnrollment functions
+function New-CSREnrollment {
+ param (
+ [string]$SubjectText,
+ [string]$ProviderName = "Microsoft Strong Cryptographic Provider",
+ [string]$KeyType,
+ [string]$KeyLength,
+ [string]$SAN
+ )
+
+ # Validate the Crypto Service Provider
+ Validate-CryptoProvider -ProviderName $ProviderName
+
+ # Build the SAN entries if provided
+ $sanContent = ""
+ if ($SAN) {
+ $sanEntries = $SAN -split "&"
+ $sanDirectives = $sanEntries | ForEach-Object { "_continue_ = `"$($_)&`"" }
+ $sanContent = @"
+[Extensions]
+2.5.29.17 = `"{text}`"
+$($sanDirectives -join "`n")
+"@
+ }
+
+ # Generate INF file content for the CSR
+ $infContent = @"
+[Version]
+Signature=`"$`Windows NT$`"
+
+[NewRequest]
+Subject = "$SubjectText"
+ProviderName = "$ProviderName"
+MachineKeySet = True
+HashAlgorithm = SHA256
+KeyAlgorithm = $KeyType
+KeyLength = $KeyLength
+KeySpec = 0
+
+$sanContent
+"@
+
+ Write-Verbose "INF Contents: $infContent"
+
+ # Path to temporary INF file
+ $infFile = [System.IO.Path]::GetTempFileName() + ".inf"
+ $csrOutputFile = [System.IO.Path]::GetTempFileName() + ".csr"
+
+ Set-Content -Path $infFile -Value $infContent
+ Write-Information "Generated INF file at: $infFile"
+
+ try {
+ # Run certreq to generate CSR
+ $certReqCommand = "certreq -new -q `"$infFile`" `"$csrOutputFile`""
+ Write-Information "Running certreq: $certReqCommand"
+
+ # Capture the output and errors
+ $certReqOutput = & certreq -new -q $infFile $csrOutputFile 2>&1
+
+ # Check the exit code of the command
+ if ($LASTEXITCODE -ne 0) {
+ $errMsg = "Certreq failed with exit code $LASTEXITCODE. Output: $certReqOutput"
+ throw $errMsg
+ }
+
+ # If successful, proceed
+ Write-Information "Certreq completed successfully."
+
+ # Read CSR file
+ if (Test-Path $csrOutputFile) {
+ $csrContent = Get-Content -Path $csrOutputFile -Raw
+ Write-Information "CSR successfully created at: $csrOutputFile"
+ return $csrContent
+ } else {
+ throw "Failed to create CSR file."
+ }
+ } catch {
+ Write-Error $_
+ } finally {
+ # Clean up temporary files
+ if (Test-Path $infFile) {
+ Remove-Item -Path $infFile -Force
+ Write-Information "Deleted temporary INF file."
+ }
+
+ if (Test-Path $csrOutputFile) {
+ Remove-Item -Path $csrOutputFile -Force
+ Write-Information "Deleted temporary CSR file."
+ }
+ }
+}
+
+function Import-SignedCertificate {
+ param (
+ [Parameter(Mandatory = $true)]
+ [byte[]]$RawData, # RawData from the certificate
+
+ [Parameter(Mandatory = $true)]
+ [ValidateSet("My", "Root", "CA", "TrustedPublisher", "TrustedPeople")]
+ [string]$StoreName # Store to which the certificate should be imported
+ )
+
+ try {
+ # Step 1: Convert raw certificate data to Base64 string with line breaks
+ Write-Verbose "Converting raw certificate data to Base64 string."
+ $csrData = [System.Convert]::ToBase64String($RawData, [System.Base64FormattingOptions]::InsertLineBreaks)
+
+ # Step 2: Create PEM-formatted certificate content
+ Write-Verbose "Creating PEM-formatted certificate content."
+ $certContent = @(
+ "-----BEGIN CERTIFICATE-----"
+ $csrData
+ "-----END CERTIFICATE-----"
+ ) -join "`n"
+
+ # Step 3: Create a temporary file for the certificate
+ Write-Verbose "Creating a temporary file for the certificate."
+ $cerFilename = [System.IO.Path]::GetTempFileName()
+ Set-Content -Path $cerFilename -Value $certContent -Force
+ Write-Verbose "Temporary certificate file created at: $cerFilename"
+
+ # Step 4: Import the certificate into the specified store
+ Write-Verbose "Importing the certificate to the store: Cert:\LocalMachine\$StoreName"
+ Set-Location -Path "Cert:\LocalMachine\$StoreName"
+
+ $importResult = Import-Certificate -FilePath $cerFilename
+ if ($importResult) {
+ Write-Verbose "Certificate successfully imported to Cert:\LocalMachine\$StoreName."
+ } else {
+ throw "Certificate import failed."
+ }
+
+ # Step 5: Cleanup temporary file
+ if (Test-Path $cerFilename) {
+ Remove-Item -Path $cerFilename -Force
+ Write-Verbose "Temporary file deleted: $cerFilename"
+ }
+
+ # Step 6: Return the imported certificate's thumbprint
+ return $importResult.Thumbprint
+
+ } catch {
+ Write-Error "An error occurred during the certificate export and import process: $_"
+ }
+}
+#####
+
+# Shared Functions
+# Function to get SAN (Subject Alternative Names) from a certificate
+function Get-KFSAN($cert) {
+ $san = $cert.Extensions | Where-Object { $_.Oid.FriendlyName -eq "Subject Alternative Name" }
+ if ($san) {
+ return ($san.Format(1) -split ", " -join "; ")
+ }
+ return $null
+}
+
+#Function to verify if the given CSP is found on the computer
+function Test-CryptoServiceProvider {
+ param(
+ [Parameter(Mandatory = $true)]
+ [string]$CSPName
+ )
+
+ # Function to get the list of installed Cryptographic Service Providers from the registry
+ function Get-CryptoServiceProviders {
+ $cspRegistryPath = "HKLM:\SOFTWARE\Microsoft\Cryptography\Defaults\Provider"
+
+ # Retrieve all CSP names from the registry
+ $providers = Get-ChildItem -Path $cspRegistryPath | Select-Object -ExpandProperty PSChildName
+
+ return $providers
+ }
+
+ # Get the list of installed CSPs
+ $installedCSPs = Get-CryptoServiceProviders
+
+ # Check if the user-provided CSP exists in the list
+ if ($installedCSPs -contains $CSPName) {
+ return $true
+ }
+ else {
+ return $false
+ }
+}
+
+# Function that takes an x509 certificate object and returns the csp
+function Get-CertificateCSP {
+ param (
+ [Parameter(Mandatory = $true)]
+ [System.Security.Cryptography.X509Certificates.X509Certificate2]$Cert
+ )
+
+ # Check if the certificate has a private key
+ if ($Cert -and $Cert.HasPrivateKey) {
+ try {
+ # Access the certificate's private key to get CSP info
+ $key = $Cert.PrivateKey
+
+ # Retrieve the Provider Name from the private key
+ $cspName = $key.Key.Provider.Provider
+
+ # Return the CSP name
+ return $cspName
+ } catch {
+ return $null
+ }
+ } else {
+ return $null
+ }
+}
+
+function Get-CryptoProviders {
+ # Retrieves the list of available Crypto Service Providers using certutil
+ try {
+ Write-Verbose "Retrieving Crypto Service Providers using certutil..."
+ $certUtilOutput = certutil -csplist
+
+ # Parse the output to extract CSP names
+ $cspInfoList = @()
+ foreach ($line in $certUtilOutput) {
+ if ($line -match "Provider Name:") {
+ $cspName = ($line -split ":")[1].Trim()
+ $cspInfoList += $cspName
+ }
+ }
+
+ if ($cspInfoList.Count -eq 0) {
+ throw "No Crypto Service Providers were found. Ensure certutil is functioning properly."
+ }
+
+ Write-Verbose "Retrieved the following CSPs:"
+ $cspInfoList | ForEach-Object { Write-Verbose $_ }
+
+ return $cspInfoList
+ } catch {
+ throw "Failed to retrieve Crypto Service Providers: $_"
+ }
+}
+
+function Validate-CryptoProvider {
+ param (
+ [Parameter(Mandatory)]
+ [string]$ProviderName
+ )
+ Write-Verbose "Validating CSP: $ProviderName"
+
+ $availableProviders = Get-CryptoProviders
+
+ if (-not ($availableProviders -contains $ProviderName)) {
+ throw "Crypto Service Provider '$ProviderName' is either invalid or not found on this system."
+ }
+
+ Write-Verbose "Crypto Service Provider '$ProviderName' is valid."
+}
\ No newline at end of file
diff --git a/IISU/CertificateStoreException.cs b/IISU/RemoteSettings.cs
similarity index 52%
rename from IISU/CertificateStoreException.cs
rename to IISU/RemoteSettings.cs
index f207566..43465b8 100644
--- a/IISU/CertificateStoreException.cs
+++ b/IISU/RemoteSettings.cs
@@ -1,4 +1,4 @@
-// Copyright 2022 Keyfactor
+// Copyright 2025 Keyfactor
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,28 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using System;
-using System.Runtime.Serialization;
+// 021225 rcp 2.6.0 Cleaned up and verified code
namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore
{
- [Serializable]
- public class CertificateStoreException : Exception
+ public class RemoteSettings
{
- public CertificateStoreException()
- {
- }
+ public string ClientMachineName { get; set; }
+ public string Protocol{ get; set; }
+ public string Port { get; set; }
+ public bool IncludePortInSPN { get; set; }
- public CertificateStoreException(string message) : base(message)
- {
- }
+ public string ServerUserName { get; set; }
+ public string ServerPassword { get; set; }
- public CertificateStoreException(string message, Exception innerException) : base(message, innerException)
- {
- }
-
- protected CertificateStoreException(SerializationInfo info, StreamingContext context) : base(info, context)
- {
- }
}
-}
\ No newline at end of file
+
+}
diff --git a/IISU/Scripts/PowerShellScripts.cs b/IISU/Scripts/PowerShellScripts.cs
deleted file mode 100644
index 3da10e3..0000000
--- a/IISU/Scripts/PowerShellScripts.cs
+++ /dev/null
@@ -1,140 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.Scripts
-{
- public class PowerShellScripts
- {
- public const string UpdateIISBindingsV6 = @"
- param (
- $SiteName, # The name of the IIS site
- $IPAddress, # The IP Address for the binding
- $Port, # The port number for the binding
- $Hostname, # Hostname for the binding (if any)
- $Protocol, # Protocol (e.g., HTTP, HTTPS)
- $Thumbprint, # Certificate thumbprint for HTTPS bindings
- $StoreName, # Certificate store location (e.g., ""My"" for personal certs)
- $SslFlags # SSL flags (if any)
- )
-
- # Set Execution Policy (optional, depending on your environment)
- Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
-
- # Check if the WebAdministration module is available
- $module = Get-Module -Name WebAdministration -ListAvailable
-
- if (-not $module) {
- throw ""The WebAdministration module is not installed on this system.""
- }
-
- # Check if the WebAdministration module is already loaded
- if (-not (Get-Module -Name WebAdministration)) {
- try {
- # Attempt to import the WebAdministration module
- Import-Module WebAdministration -ErrorAction Stop
- }
- catch {
- throw ""Failed to load the WebAdministration module. Ensure it is installed and available.""
- }
- }
-
- # Retrieve the existing binding information
- $myBinding = ""${IPAddress}:${Port}:${Hostname}""
- Write-Host ""myBinding: "" $myBinding
-
- $siteBindings = Get-IISSiteBinding -Name $SiteName
- $existingBinding = $siteBindings | Where-Object { $_.bindingInformation -eq $myBinding -and $_.protocol -eq $Protocol }
-
- Write-Host ""Binding:"" $existingBinding
-
- if ($null -ne $existingBinding) {
- # Remove the existing binding
- Remove-IISSiteBinding -Name $SiteName -BindingInformation $existingBinding.BindingInformation -Protocol $existingBinding.Protocol -Confirm:$false
-
- Write-Host ""Removed existing binding: $($existingBinding.BindingInformation)""
- }
-
- # Create the new binding with modified properties
- $newBindingInfo = ""${IPAddress}:${Port}:${Hostname}""
-
- New-IISSiteBinding -Name $SiteName `
- -BindingInformation $newBindingInfo `
- -Protocol $Protocol `
- -CertificateThumbprint $Thumbprint `
- -CertStoreLocation $StoreName `
- -SslFlag $SslFlags
-
- Write-Host ""New binding added: $newBindingInfo""";
-
- public const string UpdateIISBindingsV8 = @"
- param (
- $SiteName, # The name of the IIS site
- $IPAddress, # The IP Address for the binding
- $Port, # The port number for the binding
- $Hostname, # Hostname for the binding (if any)
- $Protocol, # Protocol (e.g., HTTP, HTTPS)
- $Thumbprint, # Certificate thumbprint for HTTPS bindings
- $StoreName, # Certificate store location (e.g., ""My"" for personal certs)
- $SslFlags # SSL flags (if any)
- )
-
- # Set Execution Policy (optional, depending on your environment)
- Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
-
- ## Check if the IISAdministration module is available
- #$module = Get-Module -Name IISAdministration -ListAvailable
-
- #if (-not $module) {
- # throw ""The IISAdministration module is not installed on this system.""
- #}
-
- # Check if the IISAdministration module is already loaded
- if (-not (Get-Module -Name IISAdministration)) {
- try {
- # Attempt to import the IISAdministration module
- Import-Module IISAdministration -ErrorAction Stop
- }
- catch {
- throw ""Failed to load the IISAdministration module. Ensure it is installed and available.""
- }
- }
-
- # Retrieve the existing binding information
- $myBinding = ""${IPAddress}:${Port}:${Hostname}""
- Write-Host ""myBinding: "" $myBinding
-
- $siteBindings = Get-IISSiteBinding -Name $SiteName
- $existingBinding = $siteBindings | Where-Object { $_.bindingInformation -eq $myBinding -and $_.protocol -eq $Protocol }
-
- Write-Host ""Binding:"" $existingBinding
-
- if ($null -ne $existingBinding) {
- # Remove the existing binding
- Remove-IISSiteBinding -Name $SiteName -BindingInformation $existingBinding.BindingInformation -Protocol $existingBinding.Protocol -Confirm:$false
-
- Write-Host ""Removed existing binding: $($existingBinding.BindingInformation)""
- }
-
- # Create the new binding with modified properties
- $newBindingInfo = ""${IPAddress}:${Port}:${Hostname}""
-
- try
- {
- New-IISSiteBinding -Name $SiteName `
- -BindingInformation $newBindingInfo `
- -Protocol $Protocol `
- -CertificateThumbprint $Thumbprint `
- -CertStoreLocation $StoreName `
- -SslFlag $SslFlags
-
- Write-Host ""New binding added: $newBindingInfo""
- }
- catch {
- throw $_
- }";
-
- }
-}
diff --git a/IISU/WinCertJobTypeBase.cs b/IISU/WinCertJobTypeBase.cs
index 0baf794..792adf3 100644
--- a/IISU/WinCertJobTypeBase.cs
+++ b/IISU/WinCertJobTypeBase.cs
@@ -1,4 +1,4 @@
-// Copyright 2022 Keyfactor
+// Copyright 2025 Keyfactor
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// 021225 rcp 2.6.0 Cleaned up and verified code
+
using Keyfactor.Orchestrators.Extensions.Interfaces;
namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore
diff --git a/IISU/WindowsCertStore.csproj b/IISU/WindowsCertStore.csproj
index 6bedf26..4f6b78e 100644
--- a/IISU/WindowsCertStore.csproj
+++ b/IISU/WindowsCertStore.csproj
@@ -25,13 +25,13 @@
+
-
@@ -40,12 +40,8 @@
PreserveNewest
+
+ Always
+
-
-
-
- 7.4.5
-
-
-
diff --git a/WinCertTestConsole/Program.cs b/WinCertTestConsole/Program.cs
index dc2c756..f62244d 100644
--- a/WinCertTestConsole/Program.cs
+++ b/WinCertTestConsole/Program.cs
@@ -14,10 +14,14 @@
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.IO;
+using System.Management.Automation;
+using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Keyfactor.Extensions.Orchestrator.WindowsCertStore.IISU;
+using Keyfactor.Extensions.Orchestrator.WindowsCertStore.WinCert;
using Keyfactor.Orchestrators.Extensions;
using Keyfactor.Orchestrators.Extensions.Interfaces;
using Moq;
@@ -75,25 +79,59 @@ private static async Task Main(string[] args)
// Display message to user to provide parameters.
Console.WriteLine("Running");
+ // Short-cut settings for WinCert
+ CaseName = "Inventory";
+ UserName = "user4";
+ Password = "Password1";
+ StorePath = "My";
+ ClientMachine = "192.168.230.170";
+ WinRmPort = "5985";
+ string StoreType = "WinCert";
+ //
+
switch (CaseName)
{
case "Inventory":
- Console.WriteLine("Running Inventory");
- InventoryJobConfiguration invJobConfig;
- invJobConfig = GetInventoryJobConfiguration();
- Console.WriteLine("Got Inventory Config");
- SubmitInventoryUpdate sui = GetItems;
- var secretResolver = new Mock();
- secretResolver.Setup(m => m.Resolve(It.Is(s => s == invJobConfig.ServerUsername)))
- .Returns(() => invJobConfig.ServerUsername);
- secretResolver.Setup(m => m.Resolve(It.Is(s => s == invJobConfig.ServerPassword)))
- .Returns(() => invJobConfig.ServerPassword);
- var inv = new Inventory(secretResolver.Object);
- Console.WriteLine("Created Inventory Object With Constructor");
- var invResponse = inv.ProcessJob(invJobConfig, sui);
- Console.WriteLine("Back From Inventory");
- Console.Write(JsonConvert.SerializeObject(invResponse));
- Console.ReadLine();
+ if (StoreType == "WinIIS")
+ {
+ Console.WriteLine("Running WinIIS Inventory");
+ InventoryJobConfiguration invJobConfig;
+ invJobConfig = GetInventoryJobConfiguration(StoreType);
+ Console.WriteLine("Got Inventory Config");
+ SubmitInventoryUpdate sui = GetItems;
+ var secretResolver = new Mock();
+ secretResolver.Setup(m => m.Resolve(It.Is(s => s == invJobConfig.ServerUsername)))
+ .Returns(() => invJobConfig.ServerUsername);
+ secretResolver.Setup(m => m.Resolve(It.Is(s => s == invJobConfig.ServerPassword)))
+ .Returns(() => invJobConfig.ServerPassword);
+ var inv = new Keyfactor.Extensions.Orchestrator.WindowsCertStore.IISU.Inventory(secretResolver.Object);
+ Console.WriteLine("Created Inventory Object With Constructor");
+ var invResponse = inv.ProcessJob(invJobConfig, sui);
+ Console.WriteLine("Back From Inventory");
+ Console.Write(JsonConvert.SerializeObject(invResponse));
+ Console.ReadLine();
+ }
+ else if(StoreType == "WinCert")
+ {
+ Console.WriteLine("Running WinCert Inventory");
+ InventoryJobConfiguration invJobConfig;
+ invJobConfig = GetInventoryJobConfiguration(StoreType);
+ Console.WriteLine("Got Inventory Config");
+ SubmitInventoryUpdate sui = GetItems;
+ var secretResolver = new Mock();
+ secretResolver.Setup(m => m.Resolve(It.Is(s => s == invJobConfig.ServerUsername)))
+ .Returns(() => invJobConfig.ServerUsername);
+ secretResolver.Setup(m => m.Resolve(It.Is(s => s == invJobConfig.ServerPassword)))
+ .Returns(() => invJobConfig.ServerPassword);
+ var inv = new Keyfactor.Extensions.Orchestrator.WindowsCertStore.WinCert.Inventory(secretResolver.Object);
+ Console.WriteLine("Created Inventory Object With Constructor");
+ var invResponse = inv.ProcessJob(invJobConfig, sui);
+ Console.WriteLine("Back From Inventory");
+ Console.Write(JsonConvert.SerializeObject(invResponse));
+ Console.ReadLine();
+
+ }
+
break;
case "Management":
@@ -162,7 +200,7 @@ private static void ProcessManagementJob(string jobType)
mgmtSecretResolver
.Setup(m => m.Resolve(It.Is(s => s == jobConfiguration.ServerPassword)))
.Returns(() => jobConfiguration.ServerPassword);
- var mgmt = new Management(mgmtSecretResolver.Object);
+ var mgmt = new Keyfactor.Extensions.Orchestrator.WindowsCertStore.IISU.Management(mgmtSecretResolver.Object);
var result = mgmt.ProcessJob(jobConfiguration);
Console.Write(JsonConvert.SerializeObject(result));
Console.ReadLine();
@@ -174,9 +212,19 @@ public static bool GetItems(IEnumerable items)
}
- public static InventoryJobConfiguration GetInventoryJobConfiguration()
+ public static InventoryJobConfiguration GetInventoryJobConfiguration(string storeType)
{
- var fileContent = File.ReadAllText("Inventory.json").Replace("UserNameGoesHere", UserName)
+ string myFileName = string.Empty;
+
+ if (storeType == "WinIIS")
+ {
+ myFileName = "WinIISInventory.json";
+ }else if (storeType == "WinCert")
+ {
+ myFileName = "WinCertInventory.json";
+ }
+
+ var fileContent = File.ReadAllText(myFileName).Replace("UserNameGoesHere", UserName)
.Replace("PasswordGoesHere", Password).Replace("StorePathGoesHere", StorePath)
.Replace("ClientMachineGoesHere", ClientMachine);
var result =
diff --git a/WinCertTestConsole/Properties/launchSettings.json b/WinCertTestConsole/Properties/launchSettings.json
index 33504c9..475bbf5 100644
--- a/WinCertTestConsole/Properties/launchSettings.json
+++ b/WinCertTestConsole/Properties/launchSettings.json
@@ -3,6 +3,9 @@
"WSL": {
"commandName": "WSL2",
"distributionName": ""
+ },
+ "WinCertTestConsole": {
+ "commandName": "Project"
}
}
}
\ No newline at end of file
diff --git a/WinCertTestConsole/WinCertInventory.json b/WinCertTestConsole/WinCertInventory.json
new file mode 100644
index 0000000..40510f3
--- /dev/null
+++ b/WinCertTestConsole/WinCertInventory.json
@@ -0,0 +1,29 @@
+{
+ "LastInventory": [
+ {
+ "Alias": "479D92068614E33B3CB84123AF76F1C40DF4B6F6",
+ "PrivateKeyEntry": true,
+ "Thumbprints": [
+ "479D92068614E33B3CB84123AF76F1C40DF4B6F6"
+ ]
+ }
+ ],
+ "CertificateStoreDetails": {
+ "ClientMachine": "vmlabsvr1",
+ "StorePath": "My",
+ "StorePassword": "",
+ "Properties": "{\"spnwithport\":\"false\",\"WinRm Protocol\":\"ssh\",\"WinRm Port\":\"5985\",\"ServerUsername\":null,\"ServerPassword\":null,\"ServerUseSsl\":\"true\"}",
+ "Type": 104
+ },
+ "JobCancelled": false,
+ "ServerError": null,
+ "JobHistoryId": 26010,
+ "RequestStatus": 1,
+ "ServerUsername": "UserNameGoesHere",
+ "ServerPassword": "PasswordGoesHere",
+ "UseSSL": true,
+ "JobProperties": null,
+ "JobTypeId": "00000000-0000-0000-0000-000000000000",
+ "JobId": "e92f7350-251c-4c0a-9e5d-9b3fdb745ca9",
+ "Capability": "CertStores.WinCert.Inventory"
+}
\ No newline at end of file
diff --git a/WinCertTestConsole/WinCertTestConsole.csproj b/WinCertTestConsole/WinCertTestConsole.csproj
index e75510b..520e844 100644
--- a/WinCertTestConsole/WinCertTestConsole.csproj
+++ b/WinCertTestConsole/WinCertTestConsole.csproj
@@ -1,35 +1,38 @@
-
-
-
- Exe
- net8.0
- AnyCPU
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Always
-
-
- Always
-
-
- Always
-
+
+
+
+ Exe
+ net8.0
+ AnyCPU
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
Always
-
-
-
-
+
+
+
+
diff --git a/WinCertTestConsole/Inventory.json b/WinCertTestConsole/WinIISInventory.json
similarity index 80%
rename from WinCertTestConsole/Inventory.json
rename to WinCertTestConsole/WinIISInventory.json
index 1d7f961..3c7d8a1 100644
--- a/WinCertTestConsole/Inventory.json
+++ b/WinCertTestConsole/WinIISInventory.json
@@ -9,10 +9,10 @@
}
],
"CertificateStoreDetails": {
- "ClientMachine": "iisbindingstest.command.local",
+ "ClientMachine": "192.168.230.170",
"StorePath": "My",
"StorePassword": "",
- "Properties": "{\"spnwithport\":\"false\",\"WinRm Protocol\":\"https\",\"WinRm Port\":\"5986\",\"ServerUsername\":null,\"ServerPassword\":null,\"ServerUseSsl\":\"true\"}",
+ "Properties": "{\"spnwithport\":\"false\",\"WinRm Protocol\":\"http\",\"WinRm Port\":\"5985\",\"ServerUsername\":null,\"ServerPassword\":null,\"ServerUseSsl\":\"true\"}",
"Type": 104
},
"JobCancelled": false,
diff --git a/WinCertUnitTests/UnitTestIISBinding.cs b/WinCertUnitTests/UnitTestIISBinding.cs
index 7c17a65..35d06c3 100644
--- a/WinCertUnitTests/UnitTestIISBinding.cs
+++ b/WinCertUnitTests/UnitTestIISBinding.cs
@@ -36,119 +36,119 @@ public UnitTestIISBinding()
[TestMethod]
public void RenewBindingCertificate()
{
- X509Certificate2 cert = new X509Certificate2(pfxPath, certPassword);
+ //X509Certificate2 cert = new X509Certificate2(pfxPath, certPassword);
- Runspace rs = PsHelper.GetClientPsRunspace("", "localhost", "", false, "", "");
+ //Runspace rs = PsHelper.GetClientPsRunspace("", "localhost", "", false, "", "");
- ClientPSIIManager IIS = new ClientPSIIManager(rs, "Default Web Site", "https", "*", "443", "", cert.Thumbprint, "My", "0");
- JobResult result = IIS.BindCertificate(cert);
- Assert.AreEqual("Success", result.Result.ToString());
+ //ClientPSIIManager IIS = new ClientPSIIManager(rs, "Default Web Site", "https", "*", "443", "", cert.Thumbprint, "My", "0");
+ //JobResult result = IIS.BindCertificate(cert);
+ //Assert.AreEqual("Success", result.Result.ToString());
}
[TestMethod]
public void UnBindCertificate()
{
- X509Certificate2 cert = new X509Certificate2(pfxPath, certPassword);
+ //X509Certificate2 cert = new X509Certificate2(pfxPath, certPassword);
- Runspace rs = PsHelper.GetClientPsRunspace("", "localhost", "", false, "", "");
- BindingNewCertificate();
+ //Runspace rs = PsHelper.GetClientPsRunspace("", "localhost", "", false, "", "");
+ //BindingNewCertificate();
- string sslFlag = "0";
- ClientPSIIManager IIS = new ClientPSIIManager(rs, "Default Web Site", "https", "*", "443", "", "", "My", sslFlag);
- JobResult result = IIS.UnBindCertificate();
+ //string sslFlag = "0";
+ //ClientPSIIManager IIS = new ClientPSIIManager(rs, "Default Web Site", "https", "*", "443", "", "", "My", sslFlag);
+ //JobResult result = IIS.UnBindCertificate();
- Assert.AreEqual("Success", result.Result.ToString());
+ //Assert.AreEqual("Success", result.Result.ToString());
}
[TestMethod]
public void BindingNewCertificate()
{
- X509Certificate2 cert = new X509Certificate2(pfxPath, certPassword);
+ //X509Certificate2 cert = new X509Certificate2(pfxPath, certPassword);
- Runspace rs = PsHelper.GetClientPsRunspace("", "localhost", "", false, "", "");
+ //Runspace rs = PsHelper.GetClientPsRunspace("", "localhost", "", false, "", "");
- string sslFlag = "0";
+ //string sslFlag = "0";
- ClientPSIIManager IIS = new ClientPSIIManager(rs, "Default Web Site", "https", "*", "443", "", "", "My", sslFlag);
- JobResult result = IIS.BindCertificate(cert);
+ //ClientPSIIManager IIS = new ClientPSIIManager(rs, "Default Web Site", "https", "*", "443", "", "", "My", sslFlag);
+ //JobResult result = IIS.BindCertificate(cert);
- Assert.AreEqual("Success", result.Result.ToString());
+ //Assert.AreEqual("Success", result.Result.ToString());
}
[TestMethod]
public void BindingNewCertificateBadSslFlag()
{
- X509Certificate2 cert = new X509Certificate2(pfxPath, certPassword);
+ //X509Certificate2 cert = new X509Certificate2(pfxPath, certPassword);
- Runspace rs = PsHelper.GetClientPsRunspace("", "localhost", "", false, "", "");
+ //Runspace rs = PsHelper.GetClientPsRunspace("", "localhost", "", false, "", "");
- string sslFlag = "909"; // known bad value
+ //string sslFlag = "909"; // known bad value
- ClientPSIIManager IIS = new ClientPSIIManager(rs, "Default Web Site", "https", "*", "443", "", "", "My", sslFlag);
- JobResult result = IIS.BindCertificate(cert);
+ //ClientPSIIManager IIS = new ClientPSIIManager(rs, "Default Web Site", "https", "*", "443", "", "", "My", sslFlag);
+ //JobResult result = IIS.BindCertificate(cert);
- Assert.AreEqual("Failure", result.Result.ToString());
+ //Assert.AreEqual("Failure", result.Result.ToString());
}
[TestMethod]
public void AddCertificate()
{
- Runspace rs = PsHelper.GetClientPsRunspace("", "localhost", "", false, "", "");
- rs.Open();
+ //Runspace rs = PsHelper.GetClientPsRunspace("", "localhost", "", false, "", "");
+ //rs.Open();
- ClientPSCertStoreManager certStoreManager = new ClientPSCertStoreManager(rs);
- JobResult result = certStoreManager.ImportPFXFile(pfxPath, certPassword, "", "My");
- rs.Close();
+ //ClientPSCertStoreManager certStoreManager = new ClientPSCertStoreManager(rs);
+ //JobResult result = certStoreManager.ImportPFXFile(pfxPath, certPassword, "", "My");
+ //rs.Close();
- Assert.AreEqual("Success", result.Result.ToString());
+ //Assert.AreEqual("Success", result.Result.ToString());
}
[TestMethod]
public void RemoveCertificate()
{
- Runspace rs = PsHelper.GetClientPsRunspace("", "localhost", "", false, "", "");
- rs.Open();
-
- ClientPSCertStoreManager certStoreManager = new ClientPSCertStoreManager(rs);
- try
- {
- certStoreManager.RemoveCertificate("0a3f880aa17c03ef2c75493497d89756cfafa165", "My");
- Assert.IsTrue(true, "Certificate was successfully removed.");
- }
- catch (Exception e)
- {
- Assert.IsFalse(false, e.Message);
- }
- rs.Close();
+ //Runspace rs = PsHelper.GetClientPsRunspace("", "localhost", "", false, "", "");
+ //rs.Open();
+
+ //ClientPSCertStoreManager certStoreManager = new ClientPSCertStoreManager(rs);
+ //try
+ //{
+ // certStoreManager.RemoveCertificate("0a3f880aa17c03ef2c75493497d89756cfafa165", "My");
+ // Assert.IsTrue(true, "Certificate was successfully removed.");
+ //}
+ //catch (Exception e)
+ //{
+ // Assert.IsFalse(false, e.Message);
+ //}
+ //rs.Close();
}
[TestMethod]
public void GetBoundCertificates()
{
- Runspace rs = PsHelper.GetClientPsRunspace("", "localhost", "", false, "", "");
- rs.Open();
- WinIISInventory IISInventory = new WinIISInventory();
- List certs = IISInventory.GetInventoryItems(rs, "My");
- rs.Close();
-
- Assert.IsNotNull(certs);
+ //Runspace rs = PsHelper.GetClientPsRunspace("", "localhost", "", false, "", "");
+ //rs.Open();
+ //WinIISInventory IISInventory = new WinIISInventory();
+ //List certs = IISInventory.GetInventoryItems(rs, "My");
+ //rs.Close();
- }
+ //Assert.IsNotNull(certs);
- [TestMethod]
- public void OrigSNIFlagZeroReturnsZero()
- {
- string expectedResult = "32";
- string result = ClientPSIIManager.MigrateSNIFlag("32");
- Assert.AreEqual(expectedResult, result);
}
[TestMethod]
- [ExpectedException(typeof(ArgumentOutOfRangeException))]
- public void InvalidSNIFlagThrowException()
+ public void GetIISInventory()
{
- string result = ClientPSIIManager.MigrateSNIFlag("Bad value");
+ Inventory inv = new();
+ RemoteSettings settings = new();
+ settings.ClientMachineName = "localMachine";
+ settings.Protocol = "ssh";
+ settings.Port = "443";
+ settings.IncludePortInSPN = false;
+ settings.ServerUserName = "administrator";
+ settings.ServerPassword = "@dminP@ssword@";
+
+ Listcerts = inv.QueryIISCertificates(settings);
}
static bool TestValidSslFlag(int sslFlag)
diff --git a/WinCertUnitTests/WinCertUnitTests.cs b/WinCertUnitTests/WinCertUnitTests.cs
new file mode 100644
index 0000000..5f99e05
--- /dev/null
+++ b/WinCertUnitTests/WinCertUnitTests.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Keyfactor.Extensions.Orchestrator.WindowsCertStore;
+using Keyfactor.Extensions.Orchestrator.WindowsCertStore.WinCert;
+using Keyfactor.Orchestrators.Extensions;
+
+namespace WinCertUnitTests
+{
+ [TestClass]
+ public class WinCertUnitTests
+ {
+ [TestMethod]
+ public void TestGetInventory()
+ {
+ Inventory inv = new();
+ RemoteSettings settings = new();
+ settings.ClientMachineName = "vmlabsvr1";
+ settings.Protocol = "http";
+ settings.Port = "5985";
+ settings.IncludePortInSPN = false;
+ settings.ServerUserName = "administrator";
+ settings.ServerPassword = "@dminP@ssword%";
+
+ // This function calls the Get-KFCertificates function and take the StoreName argument
+ List certs = inv.QueryWinCertCertificates(settings, "My");
+ }
+
+ [TestMethod]
+ public void TestAddCertificateToStore()
+ {
+
+ }
+ }
+}
diff --git a/WindowsCertStore.sln b/WindowsCertStore.sln
index 7d2f736..883ef0b 100644
--- a/WindowsCertStore.sln
+++ b/WindowsCertStore.sln
@@ -36,8 +36,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "images", "images", "{630203
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WinCertTestConsole", "WinCertTestConsole\WinCertTestConsole.csproj", "{D0F4A3CC-5236-4393-9C97-AE55ACE319F2}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinCertUnitTests", "WinCertUnitTests\WinCertUnitTests.csproj", "{AEE85A79-8614-447A-B14A-FD5A389C510B}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -61,14 +59,6 @@ Global
{D0F4A3CC-5236-4393-9C97-AE55ACE319F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D0F4A3CC-5236-4393-9C97-AE55ACE319F2}.Release|Any CPU.Build.0 = Release|Any CPU
{D0F4A3CC-5236-4393-9C97-AE55ACE319F2}.Release|x64.ActiveCfg = Release|Any CPU
- {AEE85A79-8614-447A-B14A-FD5A389C510B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {AEE85A79-8614-447A-B14A-FD5A389C510B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AEE85A79-8614-447A-B14A-FD5A389C510B}.Debug|x64.ActiveCfg = Debug|Any CPU
- {AEE85A79-8614-447A-B14A-FD5A389C510B}.Debug|x64.Build.0 = Debug|Any CPU
- {AEE85A79-8614-447A-B14A-FD5A389C510B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {AEE85A79-8614-447A-B14A-FD5A389C510B}.Release|Any CPU.Build.0 = Release|Any CPU
- {AEE85A79-8614-447A-B14A-FD5A389C510B}.Release|x64.ActiveCfg = Release|Any CPU
- {AEE85A79-8614-447A-B14A-FD5A389C510B}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/integration-manifest.json b/integration-manifest.json
index 4b555b0..86a9933 100644
--- a/integration-manifest.json
+++ b/integration-manifest.json
@@ -1,495 +1,448 @@
{
- "$schema": "https://keyfactor.github.io/v2/integration-manifest-schema.json",
- "integration_type": "orchestrator",
- "name": "Windows Certificate Orchestrator",
- "status": "production",
- "link_github": true,
- "release_dir": "IISU/bin/Release",
- "release_project": "IISU/WindowsCertStore.csproj",
- "update_catalog": true,
- "support_level": "kf-supported",
- "description": "The Windows Certificate Store Orchestrator Extension implements two certificate store types. 1) \u201cWinCert\u201d which manages certificates in a Windows local machine store, and 2) \u201cIISU\u201d which manages certificates and their bindings in a Windows local machine store that are bound to Internet Information Server (IIS) websites. These extensions replace the now deprecated \u201cIIS\u201d cert store type that ships with Keyfactor Command. The \u201cIISU\u201d extension also replaces the \u201cIISBin\u201d certificate store type from prior versions of this repository. This orchestrator extension is in the process of being renamed from \u201cIIS Orchestrator\u201d as it now supports certificates that are not in use by IIS.",
- "about": {
- "orchestrator": {
- "UOFramework": "10.1",
- "pam_support": true,
- "keyfactor_platform_version": "9.10",
- "win": {
- "supportsCreateStore": false,
- "supportsDiscovery": false,
- "supportsManagementAdd": true,
- "supportsManagementRemove": true,
- "supportsReenrollment": true,
- "supportsInventory": true,
- "platformSupport": "Unused"
+ "$schema": "https://keyfactor.github.io/integration-manifest-schema.json",
+ "integration_type": "orchestrator",
+ "name": "WinCertStore Orchestrator",
+ "status": "production",
+ "link_github": true,
+ "release_dir": "IISU/bin/Release",
+ "update_catalog": true,
+ "support_level": "kf-supported",
+ "description": "The Windows Certificate Store Orchestrator Extension implements two certificate store types. 1) “WinCert” which manages certificates in a Windows local machine store, and 2) “IISU” which manages certificates and their bindings in a Windows local machine store that are bound to Internet Information Server (IIS) websites. These extensions replace the now deprecated “IIS” cert store type that ships with Keyfactor Command. The “IISU” extension also replaces the “IISBin” certificate store type from prior versions of this repository. This orchestrator extension is in the process of being renamed from “IIS Orchestrator” as it now supports certificates that are not in use by IIS.",
+ "about": {
+ "orchestrator": {
+ "UOFramework": "10.1",
+ "pam_support": true,
+ "keyfactor_platform_version": "9.10",
+ "win": {
+ "supportsCreateStore": false,
+ "supportsDiscovery": false,
+ "supportsManagementAdd": true,
+ "supportsManagementRemove": true,
+ "supportsReenrollment": true,
+ "supportsInventory": true,
+ "platformSupport": "Unused"
+ },
+ "linux": {
+ "supportsCreateStore": false,
+ "supportsDiscovery": false,
+ "supportsManagementAdd": false,
+ "supportsManagementRemove": false,
+ "supportsReenrollment": false,
+ "supportsInventory": false,
+ "platformSupport": "Unused"
+ },
+ "store_types": [
+ {
+ "Name": "Windows Certificate",
+ "ShortName": "WinCert",
+ "Capability": "WinCert",
+ "LocalStore": false,
+ "SupportedOperations": {
+ "Add": true,
+ "Create": false,
+ "Discovery": false,
+ "Enrollment": true,
+ "Remove": true
+ },
+ "Properties": [
+ {
+ "Name": "spnwithport",
+ "DisplayName": "SPN With Port",
+ "Type": "Bool",
+ "DependsOn": "",
+ "DefaultValue": "false",
+ "Required": false
},
- "linux": {
- "supportsCreateStore": false,
- "supportsDiscovery": false,
- "supportsManagementAdd": false,
- "supportsManagementRemove": false,
- "supportsReenrollment": false,
- "supportsInventory": false,
- "platformSupport": "Unused"
+ {
+ "Name": "WinRM Protocol",
+ "DisplayName": "WinRM Protocol",
+ "Type": "MultipleChoice",
+ "DependsOn": "",
+ "DefaultValue": "https,http",
+ "Required": true
},
- "store_types": [
- {
- "Name": "Windows Certificate",
- "ShortName": "WinCert",
- "Capability": "WinCert",
- "LocalStore": false,
- "SupportedOperations": {
- "Add": true,
- "Create": false,
- "Discovery": false,
- "Enrollment": true,
- "Remove": true
- },
- "Properties": [
- {
- "Name": "spnwithport",
- "DisplayName": "SPN With Port",
- "Type": "Bool",
- "DependsOn": "",
- "DefaultValue": "false",
- "Required": false,
- "Description": "Internally set the -IncludePortInSPN option when creating the remote PowerShell connection. Needed for some Kerberos configurations."
- },
- {
- "Name": "WinRM Protocol",
- "DisplayName": "WinRM Protocol",
- "Type": "MultipleChoice",
- "DependsOn": "",
- "DefaultValue": "https,http",
- "Required": true,
- "Description": "Multiple choice value specifying the protocol (https or http) that the target server's WinRM listener is using. Example: 'https' to use secure communication."
- },
- {
- "Name": "WinRM Port",
- "DisplayName": "WinRM Port",
- "Type": "String",
- "DependsOn": "",
- "DefaultValue": "5986",
- "Required": true,
- "Description": "String value specifying the port number that the target server's WinRM listener is configured to use. Example: '5986' for HTTPS or '5985' for HTTP."
- },
- {
- "Name": "ServerUsername",
- "DisplayName": "Server Username",
- "Type": "Secret",
- "DependsOn": "",
- "DefaultValue": "",
- "Required": false,
- "Description": "Username used to log into the target server for establishing the WinRM session. Example: 'administrator' or 'domain\\username'."
- },
- {
- "Name": "ServerPassword",
- "DisplayName": "Server Password",
- "Type": "Secret",
- "DependsOn": "",
- "DefaultValue": "",
- "Required": false,
- "Description": "Password corresponding to the Server Username used to log into the target server for establishing the WinRM session. Example: 'P@ssw0rd123'."
- },
- {
- "Name": "ServerUseSsl",
- "DisplayName": "Use SSL",
- "Type": "Bool",
- "DependsOn": "",
- "DefaultValue": "true",
- "Required": true,
- "Description": "Determine whether the server uses SSL or not (This field is automatically created)"
- }
- ],
- "EntryParameters": [
- {
- "Name": "ProviderName",
- "DisplayName": "Crypto Provider Name",
- "Type": "String",
- "RequiredWhen": {
- "HasPrivateKey": false,
- "OnAdd": false,
- "OnRemove": false,
- "OnReenrollment": false
- },
- "DependsOn": "",
- "DefaultValue": "",
- "Options": "",
- "Description": "Name of the Windows cryptographic provider to use during reenrollment jobs when generating and storing the private keys. If not specified, defaults to 'Microsoft Strong Cryptographic Provider'. This value would typically be specified when leveraging a Hardware Security Module (HSM). The specified cryptographic provider must be available on the target server being managed. The list of installed cryptographic providers can be obtained by running 'certutil -csplist' on the target Server."
- },
- {
- "Name": "SAN",
- "DisplayName": "SAN",
- "Type": "String",
- "RequiredWhen": {
- "HasPrivateKey": false,
- "OnAdd": false,
- "OnRemove": false,
- "OnReenrollment": true
- },
- "DependsOn": "",
- "DefaultValue": "",
- "Options": "",
- "Description": "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."
- }
- ],
- "PasswordOptions": {
- "EntrySupported": false,
- "StoreRequired": false,
- "Style": "Default"
- },
- "StorePathValue": "",
- "PrivateKeyAllowed": "Optional",
- "ServerRequired": true,
- "PowerShell": false,
- "BlueprintAllowed": false,
- "CustomAliasAllowed": "Forbidden",
- "ClientMachineDescription": "Hostname of the Windows Server containing the certificate store to be managed. If this value is a hostname, a WinRM session will be established using the credentials specified in the Server Username and Server Password fields. For more information, see [Client Machine](#note-regarding-client-machine).",
- "StorePathDescription": "Windows certificate store path to manage. The store must exist in the Local Machine store on the target server, e.g., 'My' for the Personal Store or 'Root' for the Trusted Root Certification Authorities Store."
- },
- {
- "Name": "IIS Bound Certificate",
- "ShortName": "IISU",
- "Capability": "IISU",
- "LocalStore": false,
- "SupportedOperations": {
- "Add": true,
- "Create": false,
- "Discovery": false,
- "Enrollment": true,
- "Remove": true
- },
- "Properties": [
- {
- "Name": "spnwithport",
- "DisplayName": "SPN With Port",
- "Type": "Bool",
- "DependsOn": "",
- "DefaultValue": "false",
- "Required": false,
- "Description": "Internally set the -IncludePortInSPN option when creating the remote PowerShell connection. Needed for some Kerberos configurations."
- },
- {
- "Name": "WinRM Protocol",
- "DisplayName": "WinRM Protocol",
- "Type": "MultipleChoice",
- "DependsOn": "",
- "DefaultValue": "https,http",
- "Required": true,
- "Description": "Multiple choice value specifying the protocol (https or http) that the target server's WinRM listener is using. Example: 'https' to use secure communication."
- },
- {
- "Name": "WinRM Port",
- "DisplayName": "WinRM Port",
- "Type": "String",
- "DependsOn": "",
- "DefaultValue": "5986",
- "Required": true,
- "Description": "String value specifying the port number that the target server's WinRM listener is configured to use. Example: '5986' for HTTPS or '5985' for HTTP."
- },
- {
- "Name": "ServerUsername",
- "DisplayName": "Server Username",
- "Type": "Secret",
- "DependsOn": "",
- "DefaultValue": "",
- "Required": false,
- "Description": "Username used to log into the target server for establishing the WinRM session. Example: 'administrator' or 'domain\\username'."
- },
- {
- "Name": "ServerPassword",
- "DisplayName": "Server Password",
- "Type": "Secret",
- "DependsOn": "",
- "DefaultValue": "",
- "Required": false,
- "Description": "Password corresponding to the Server Username used to log into the target server for establishing the WinRM session. Example: 'P@ssw0rd123'."
- },
- {
- "Name": "ServerUseSsl",
- "DisplayName": "Use SSL",
- "Type": "Bool",
- "DependsOn": "",
- "DefaultValue": "true",
- "Required": true,
- "Description": "Determine whether the server uses SSL or not (This field is automatically created)"
- }
- ],
- "EntryParameters": [
- {
- "Name": "Port",
- "DisplayName": "Port",
- "Type": "String",
- "RequiredWhen": {
- "HasPrivateKey": false,
- "OnAdd": false,
- "OnRemove": false,
- "OnReenrollment": false
- },
- "DependsOn": "",
- "DefaultValue": "443",
- "Options": "",
- "Description": "String value specifying the IP port to bind the certificate to for the IIS site. Example: '443' for HTTPS."
- },
- {
- "Name": "IPAddress",
- "DisplayName": "IP Address",
- "Type": "String",
- "RequiredWhen": {
- "HasPrivateKey": false,
- "OnAdd": true,
- "OnRemove": true,
- "OnReenrollment": true
- },
- "DependsOn": "",
- "DefaultValue": "*",
- "Options": "",
- "Description": "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."
- },
- {
- "Name": "HostName",
- "DisplayName": "Host Name",
- "Type": "String",
- "RequiredWhen": {
- "HasPrivateKey": false,
- "OnAdd": false,
- "OnRemove": false,
- "OnReenrollment": false
- },
- "DependsOn": "",
- "DefaultValue": "",
- "Options": "",
- "Description": "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'."
- },
- {
- "Name": "SiteName",
- "DisplayName": "IIS Site Name",
- "Type": "String",
- "RequiredWhen": {
- "HasPrivateKey": false,
- "OnAdd": true,
- "OnRemove": true,
- "OnReenrollment": true
- },
- "DependsOn": "",
- "DefaultValue": "Default Web Site",
- "Options": "",
- "Description": "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'."
- },
- {
- "Name": "SniFlag",
- "DisplayName": "SSL Flags",
- "Type": "String",
- "RequiredWhen": {
- "HasPrivateKey": false,
- "OnAdd": false,
- "OnRemove": false,
- "OnReenrollment": false
- },
- "DependsOn": "",
- "DefaultValue": "0",
- "Options": "",
- "Description": "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.)"
- },
- {
- "Name": "Protocol",
- "DisplayName": "Protocol",
- "Type": "MultipleChoice",
- "RequiredWhen": {
- "HasPrivateKey": false,
- "OnAdd": true,
- "OnRemove": true,
- "OnReenrollment": true
- },
- "DependsOn": "",
- "DefaultValue": "https",
- "Options": "https,http",
- "Description": "Multiple choice value specifying the protocol to bind to. Example: 'https' for secure communication."
- },
- {
- "Name": "ProviderName",
- "DisplayName": "Crypto Provider Name",
- "Type": "String",
- "RequiredWhen": {
- "HasPrivateKey": false,
- "OnAdd": false,
- "OnRemove": false,
- "OnReenrollment": false
- },
- "DependsOn": "",
- "DefaultValue": "",
- "Options": "",
- "Description": "Name of the Windows cryptographic provider to use during reenrollment jobs when generating and storing the private keys. If not specified, defaults to 'Microsoft Strong Cryptographic Provider'. This value would typically be specified when leveraging a Hardware Security Module (HSM). The specified cryptographic provider must be available on the target server being managed. The list of installed cryptographic providers can be obtained by running 'certutil -csplist' on the target Server."
- },
- {
- "Name": "SAN",
- "DisplayName": "SAN",
- "Type": "String",
- "RequiredWhen": {
- "HasPrivateKey": false,
- "OnAdd": false,
- "OnRemove": false,
- "OnReenrollment": true
- },
- "DependsOn": "",
- "DefaultValue": "",
- "Options": "",
- "Description": "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."
- }
- ],
- "PasswordOptions": {
- "EntrySupported": false,
- "StoreRequired": false,
- "Style": "Default"
- },
- "StorePathValue": "[\"My\",\"WebHosting\"]",
- "PrivateKeyAllowed": "Required",
- "ServerRequired": true,
- "PowerShell": false,
- "BlueprintAllowed": false,
- "CustomAliasAllowed": "Forbidden",
- "ClientMachineDescription": "Hostname of the Windows Server containing the IIS certificate store to be managed. If this value is a hostname, a WinRM session will be established using the credentials specified in the Server Username and Server Password fields. For more information, see [Client Machine](#note-regarding-client-machine).",
- "StorePathDescription": "Windows certificate store path to manage. Choose 'My' for the Personal store or 'WebHosting' for the Web Hosting store."
- },
- {
- "Name": "WinSql",
- "ShortName": "WinSql",
- "Capability": "WinSql",
- "LocalStore": false,
- "SupportedOperations": {
- "Add": true,
- "Create": false,
- "Discovery": false,
- "Enrollment": false,
- "Remove": true
- },
- "Properties": [
- {
- "Name": "spnwithport",
- "DisplayName": "SPN With Port",
- "Type": "Bool",
- "DependsOn": "",
- "DefaultValue": "false",
- "Required": false,
- "Description": "Internally set the -IncludePortInSPN option when creating the remote PowerShell connection. Needed for some Kerberos configurations."
- },
- {
- "Name": "WinRM Protocol",
- "DisplayName": "WinRM Protocol",
- "Type": "MultipleChoice",
- "DependsOn": "",
- "DefaultValue": "https,http",
- "Required": true,
- "Description": "Multiple choice value specifying the protocol (https or http) that the target server's WinRM listener is using. Example: 'https' to use secure communication."
- },
- {
- "Name": "WinRM Port",
- "DisplayName": "WinRM Port",
- "Type": "String",
- "DependsOn": "",
- "DefaultValue": "5986",
- "Required": true,
- "Description": "String value specifying the port number that the target server's WinRM listener is configured to use. Example: '5986' for HTTPS or '5985' for HTTP."
- },
- {
- "Name": "ServerUsername",
- "DisplayName": "Server Username",
- "Type": "Secret",
- "DependsOn": "",
- "DefaultValue": "",
- "Required": false,
- "Description": "Username used to log into the target server for establishing the WinRM session. Example: 'administrator' or 'domain\\username'."
- },
- {
- "Name": "ServerPassword",
- "DisplayName": "Server Password",
- "Type": "Secret",
- "DependsOn": "",
- "DefaultValue": "",
- "Required": false,
- "Description": "Password corresponding to the Server Username used to log into the target server for establishing the WinRM session. Example: 'P@ssw0rd123'."
- },
- {
- "Name": "ServerUseSsl",
- "DisplayName": "Use SSL",
- "Type": "Bool",
- "DependsOn": "",
- "DefaultValue": "true",
- "Required": true,
- "Description": "Determine whether the server uses SSL or not (This field is automatically created)"
- },
- {
- "Name": "RestartService",
- "DisplayName": "Restart SQL Service After Cert Installed",
- "Type": "Bool",
- "DependsOn": "",
- "DefaultValue": "false",
- "Required": true,
- "Description": "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."
- }
- ],
- "EntryParameters": [
- {
- "Name": "InstanceName",
- "DisplayName": "Instance Name",
- "Type": "String",
- "RequiredWhen": {
- "HasPrivateKey": false,
- "OnAdd": false,
- "OnRemove": false,
- "OnReenrollment": false
- },
- "Description": "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."
- },
- {
- "Name": "ProviderName",
- "DisplayName": "Crypto Provider Name",
- "Type": "String",
- "RequiredWhen": {
- "HasPrivateKey": false,
- "OnAdd": false,
- "OnRemove": false,
- "OnReenrollment": false
- },
- "DependsOn": "",
- "DefaultValue": "",
- "Options": "",
- "Description": "Optional string value specifying the name of the Windows cryptographic provider to use during reenrollment jobs when generating and storing private keys. Example: 'Microsoft Strong Cryptographic Provider'."
- },
- {
- "Name": "SAN",
- "DisplayName": "SAN",
- "Type": "String",
- "RequiredWhen": {
- "HasPrivateKey": false,
- "OnAdd": false,
- "OnRemove": false,
- "OnReenrollment": true
- },
- "DependsOn": "",
- "DefaultValue": "",
- "Options": "",
- "Description": "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."
- }
- ],
- "PasswordOptions": {
- "EntrySupported": false,
- "StoreRequired": false,
- "Style": "Default"
- },
- "StorePathValue": "My",
- "PrivateKeyAllowed": "Optional",
- "JobProperties": [
- "InstanceName"
- ],
- "ServerRequired": true,
- "PowerShell": false,
- "BlueprintAllowed": true,
- "CustomAliasAllowed": "Forbidden",
- "ClientMachineDescription": "Hostname of the Windows Server containing the SQL Server Certificate Store to be managed. If this value is a hostname, a WinRM session will be established using the credentials specified in the Server Username and Server Password fields. For more information, see [Client Machine](#note-regarding-client-machine).",
- "StorePathDescription": "Fixed string value 'My' indicating the Personal store on the Local Machine. This denotes the Windows certificate store to be managed for SQL Server."
- }
- ]
+ {
+ "Name": "WinRM Port",
+ "DisplayName": "WinRM Port",
+ "Type": "String",
+ "DependsOn": "",
+ "DefaultValue": "5986",
+ "Required": true
+ },
+ {
+ "Name": "ServerUsername",
+ "DisplayName": "Server Username",
+ "Type": "Secret",
+ "DependsOn": "",
+ "DefaultValue": null,
+ "Required": false
+ },
+ {
+ "Name": "ServerPassword",
+ "DisplayName": "Server Password",
+ "Type": "Secret",
+ "DependsOn": "",
+ "DefaultValue": null,
+ "Required": false
+ },
+ {
+ "Name": "ServerUseSsl",
+ "DisplayName": "Use SSL",
+ "Type": "Bool",
+ "DependsOn": "",
+ "DefaultValue": "true",
+ "Required": true
+ }
+ ],
+ "EntryParameters": [
+ {
+ "Name": "ProviderName",
+ "DisplayName": "Crypto Provider Name",
+ "Type": "String",
+ "RequiredWhen": {
+ "HasPrivateKey": false,
+ "OnAdd": false,
+ "OnRemove": false,
+ "OnReenrollment": false
+ },
+ "DependsOn": "",
+ "DefaultValue": "",
+ "Options": ""
+ },
+ {
+ "Name": "SAN",
+ "DisplayName": "SAN",
+ "Type": "String",
+ "RequiredWhen": {
+ "HasPrivateKey": false,
+ "OnAdd": false,
+ "OnRemove": false,
+ "OnReenrollment": true
+ },
+ "DependsOn": "",
+ "DefaultValue": "",
+ "Options": ""
+ }
+ ],
+ "PasswordOptions": {
+ "EntrySupported": false,
+ "StoreRequired": false,
+ "Style": "Default"
+ },
+ "StorePathValue": "",
+ "PrivateKeyAllowed": "Optional",
+ "ServerRequired": true,
+ "PowerShell": false,
+ "BlueprintAllowed": false,
+ "CustomAliasAllowed": "Forbidden"
+ },
+ {
+ "Name": "IIS Bound Certificate",
+ "ShortName": "IISU",
+ "Capability": "IISU",
+ "LocalStore": false,
+ "SupportedOperations": {
+ "Add": true,
+ "Create": false,
+ "Discovery": false,
+ "Enrollment": true,
+ "Remove": true
+ },
+ "Properties": [
+ {
+ "Name": "spnwithport",
+ "DisplayName": "SPN With Port",
+ "Type": "Bool",
+ "DependsOn": "",
+ "DefaultValue": "false",
+ "Required": false
+ },
+ {
+ "Name": "WinRm Protocol",
+ "DisplayName": "WinRm Protocol",
+ "Type": "MultipleChoice",
+ "DependsOn": "",
+ "DefaultValue": "https,http",
+ "Required": true
+ },
+ {
+ "Name": "WinRm Port",
+ "DisplayName": "WinRm Port",
+ "Type": "String",
+ "DependsOn": "",
+ "DefaultValue": "5986",
+ "Required": true
+ },
+ {
+ "Name": "ServerUsername",
+ "DisplayName": "Server Username",
+ "Type": "Secret",
+ "DependsOn": "",
+ "DefaultValue": null,
+ "Required": false
+ },
+ {
+ "Name": "ServerPassword",
+ "DisplayName": "Server Password",
+ "Type": "Secret",
+ "DependsOn": "",
+ "DefaultValue": null,
+ "Required": false
+ },
+ {
+ "Name": "ServerUseSsl",
+ "DisplayName": "Use SSL",
+ "Type": "Bool",
+ "DependsOn": "",
+ "DefaultValue": "true",
+ "Required": true
+ }
+ ],
+ "EntryParameters": [
+ {
+ "Name": "Port",
+ "DisplayName": "Port",
+ "Type": "String",
+ "RequiredWhen": {
+ "HasPrivateKey": false,
+ "OnAdd": false,
+ "OnRemove": false,
+ "OnReenrollment": false
+ },
+ "DependsOn": "",
+ "DefaultValue": "443",
+ "Options": ""
+ },
+ {
+ "Name": "IPAddress",
+ "DisplayName": "IP Address",
+ "Type": "String",
+ "RequiredWhen": {
+ "HasPrivateKey": false,
+ "OnAdd": true,
+ "OnRemove": true,
+ "OnReenrollment": true
+ },
+ "DependsOn": "",
+ "DefaultValue": "*",
+ "Options": ""
+ },
+ {
+ "Name": "HostName",
+ "DisplayName": "Host Name",
+ "Type": "String",
+ "RequiredWhen": {
+ "HasPrivateKey": false,
+ "OnAdd": false,
+ "OnRemove": false,
+ "OnReenrollment": false
+ },
+ "DependsOn": "",
+ "DefaultValue": "",
+ "Options": ""
+ },
+ {
+ "Name": "SiteName",
+ "DisplayName": "IIS Site Name",
+ "Type": "String",
+ "RequiredWhen": {
+ "HasPrivateKey": false,
+ "OnAdd": true,
+ "OnRemove": true,
+ "OnReenrollment": true
+ },
+ "DependsOn": "",
+ "DefaultValue": "Default Web Site",
+ "Options": ""
+ },
+ {
+ "Name": "SniFlag",
+ "DisplayName": "SNI Support",
+ "Type": "String",
+ "RequiredWhen": {
+ "HasPrivateKey": false,
+ "OnAdd": false,
+ "OnRemove": false,
+ "OnReenrollment": false
+ },
+ "DependsOn": "",
+ "DefaultValue": "0",
+ "Options": ""
+ },
+ {
+ "Name": "Protocol",
+ "DisplayName": "Protocol",
+ "Type": "MultipleChoice",
+ "RequiredWhen": {
+ "HasPrivateKey": false,
+ "OnAdd": true,
+ "OnRemove": true,
+ "OnReenrollment": true
+ },
+ "DependsOn": "",
+ "DefaultValue": "https",
+ "Options": "https,http"
+ },
+ {
+ "Name": "ProviderName",
+ "DisplayName": "Crypto Provider Name",
+ "Type": "String",
+ "RequiredWhen": {
+ "HasPrivateKey": false,
+ "OnAdd": false,
+ "OnRemove": false,
+ "OnReenrollment": false
+ },
+ "DependsOn": "",
+ "DefaultValue": "",
+ "Options": ""
+ },
+ {
+ "Name": "SAN",
+ "DisplayName": "SAN",
+ "Type": "String",
+ "RequiredWhen": {
+ "HasPrivateKey": false,
+ "OnAdd": false,
+ "OnRemove": false,
+ "OnReenrollment": true
+ },
+ "DependsOn": "",
+ "DefaultValue": "",
+ "Options": ""
+ }
+ ],
+ "PasswordOptions": {
+ "EntrySupported": false,
+ "StoreRequired": false,
+ "Style": "Default"
+ },
+ "StorePathValue": "[\"My\",\"WebHosting\"]",
+ "PrivateKeyAllowed": "Required",
+ "ServerRequired": true,
+ "PowerShell": false,
+ "BlueprintAllowed": false,
+ "CustomAliasAllowed": "Forbidden"
+ },
+ {
+ "Name": "WinSql",
+ "ShortName": "WinSql",
+ "Capability": "WinSql",
+ "LocalStore": false,
+ "SupportedOperations": {
+ "Add": true,
+ "Create": false,
+ "Discovery": false,
+ "Enrollment": false,
+ "Remove": true
+ },
+ "Properties": [
+ {
+ "Name": "WinRm Protocol",
+ "DisplayName": "WinRm Protocol",
+ "Type": "MultipleChoice",
+ "DependsOn": null,
+ "DefaultValue": "https,http",
+ "Required": true
+ },
+ {
+ "Name": "WinRm Port",
+ "DisplayName": "WinRm Port",
+ "Type": "String",
+ "DependsOn": null,
+ "DefaultValue": "5986",
+ "Required": true
+ },
+ {
+ "Name": "ServerUsername",
+ "DisplayName": "Server Username",
+ "Type": "Secret",
+ "DependsOn": null,
+ "DefaultValue": null,
+ "Required": false
+ },
+ {
+ "Name": "ServerPassword",
+ "DisplayName": "Server Password",
+ "Type": "Secret",
+ "DependsOn": null,
+ "DefaultValue": null,
+ "Required": false
+ },
+ {
+ "Name": "ServerUseSsl",
+ "DisplayName": "Use SSL",
+ "Type": "Bool",
+ "DependsOn": null,
+ "DefaultValue": "true",
+ "Required": true
+ },
+ {
+ "Name": "RestartService",
+ "DisplayName": "Restart SQL Service After Cert Installed",
+ "Type": "Bool",
+ "DependsOn": null,
+ "DefaultValue": "false",
+ "Required": true
+ }
+ ],
+ "EntryParameters": [
+ {
+ "Name": "InstanceName",
+ "DisplayName": "Instance Name",
+ "Type": "String",
+ "RequiredWhen": {
+ "HasPrivateKey": false,
+ "OnAdd": false,
+ "OnRemove": false,
+ "OnReenrollment": false
+ }
+ },
+ {
+ "Name": "ProviderName",
+ "DisplayName": "Crypto Provider Name",
+ "Type": "String",
+ "RequiredWhen": {
+ "HasPrivateKey": false,
+ "OnAdd": false,
+ "OnRemove": false,
+ "OnReenrollment": false
+ },
+ "DependsOn": "",
+ "DefaultValue": "",
+ "Options": ""
+ },
+ {
+ "Name": "SAN",
+ "DisplayName": "SAN",
+ "Type": "String",
+ "RequiredWhen": {
+ "HasPrivateKey": false,
+ "OnAdd": false,
+ "OnRemove": false,
+ "OnReenrollment": true
+ },
+ "DependsOn": "",
+ "DefaultValue": "",
+ "Options": ""
+ }
+ ],
+ "PasswordOptions": {
+ "EntrySupported": false,
+ "StoreRequired": false,
+ "Style": "Default"
+ },
+ "StorePathValue": "My",
+ "PrivateKeyAllowed": "Optional",
+ "JobProperties": [
+ "InstanceName"
+ ],
+ "ServerRequired": true,
+ "PowerShell": false,
+ "BlueprintAllowed": true,
+ "CustomAliasAllowed": "Forbidden"
}
+ ]
}
-}
\ No newline at end of file
+ }
+}