From 748bd870fc4518781be63577ec2ecdf348f0ea12 Mon Sep 17 00:00:00 2001 From: Derek Dunn Date: Thu, 10 Aug 2023 12:22:13 -0400 Subject: [PATCH 1/6] Create legacy IIS to IISU/WinCert upgrade scripts and documentation --- LegacyIISMigrationGuide.md | 162 +++++++ Migration-Scripts/CreateIISUCertStoreType.sql | 454 ++++++++++++++++++ Migration-Scripts/CreateWinCertStoreType.sql | 314 ++++++++++++ Migration-Scripts/DeleteIISStores.sql | 98 ++++ .../UpgradeIISPersonalToIISU.sql | 231 +++++++++ .../UpgradeIISPersonalToWinCert.sql | 224 +++++++++ .../UpgradeIISRevokedAndRootsToWinCert.sql | 196 ++++++++ 7 files changed, 1679 insertions(+) create mode 100644 LegacyIISMigrationGuide.md create mode 100644 Migration-Scripts/CreateIISUCertStoreType.sql create mode 100644 Migration-Scripts/CreateWinCertStoreType.sql create mode 100644 Migration-Scripts/DeleteIISStores.sql create mode 100644 Migration-Scripts/UpgradeIISPersonalToIISU.sql create mode 100644 Migration-Scripts/UpgradeIISPersonalToWinCert.sql create mode 100644 Migration-Scripts/UpgradeIISRevokedAndRootsToWinCert.sql diff --git a/LegacyIISMigrationGuide.md b/LegacyIISMigrationGuide.md new file mode 100644 index 0000000..84115a7 --- /dev/null +++ b/LegacyIISMigrationGuide.md @@ -0,0 +1,162 @@ +# Built-in IIS Certificate Store Type Migration Guide + +As of Keyfactor Command v11, the built-in IIS certificate store types (IIS Personal, IIS Roots, and IIS Revoked) have been deprecated. This guide will instruct you on how to migrate the built-in IIS certificate store types to the open source iis-orchestrator certificate store types (IISU and WinCert) that can be found on the [Keyfactor GitHub page](https://github.com/Keyfactor/iis-orchestrator). + +# Prerequisites + +- All orchestrators that are currently being used to orchestrate the built-in IIS store types must support the certificate store type that the built-in stores are being migrated to (IISU, WinCert, or both). +- Ensure that you have a restorable database backup created before you begin the upgrade. + +# Migration Scripts Usage + +There are six SQL scripts that are needed for this migration: + +
+ +Creation Scripts + +## CreateIISUCertStoreType + +This script creates the IISU certificate store type. + +## CreateWinCertStoreType + +This script creates the WinCert certificate store type. + +
+ +
+ +Upgrade Scripts + +## UpgradeIISRevokedAndRootsToWinCert + +This script creates a 'WinCert' certificate store copy for every 'IIS Revoked' and 'IIS Roots' certificate store. It will also create a 'WinCert' version of every 'IIS Revoked' and 'IIS Roots' certificate store container. + +**Notes** + +- This script does not delete the IIS certificate stores or containers. +- By default, the orchestrator will use its service account credentials to connect to the new certificate stores. If other credentials should be used instead, they should be configured for each store from the Command Certificate Stores page. + +This script accepts three parameters that allow configuration of WinRM: + +| Parameter | Type | Valid Values | Default Value| Description | +|----|----|----|----|----| +|@winrm_protocol|NVARCHAR(5)|'https' or 'http'|https|The protocol that WinRM will use for interacting with the certificate stores| +|@winrm_port|INT|1 - 65535|5986|The port that WinRM will use for interacting with the certificate stores| +|@spnwithport|NVARCHAR(5)|'true' or 'false'|false|If set to 'true,' the `-IncludePortInSPN` flag will be set when WinRM creates the remote PowerShell connection| + +## UpgradeIISPersonalToIISU + +This script creates an 'IISU' certificate store copy for a provided list of 'IIS Personal' certificate stores. It will also create an 'IISU' version of every 'IIS Personal' certificate store container. + +**Notes** + +- This script does not delete the IIS certificate stores or containers. +- By default, the orchestrator will use its service account credentials to connect to the new certificate stores. If other credentials should be used instead, they should be configured for each store from the Command Certificate Stores page. + + +This script accepts four parameters: + +| Parameter | Type | Valid Values | Default Value| Description | +|----|----|----|----|----| +|@comma_separated_store_ids|NVARCHAR(MAX)|* or a comma separated list of certificate store IDs. ex: 6A79C7A3-1A9B-413B-9C6E-571EA52B9E31,3929B040-299C-4EDF-98A4-EF0FFC21DAF9,C8A62749-10C3-4441-A0CC-AD83CA9051B5||A comma separated list of IDs of the IIS Personal certificate stores that you would like to migrate. If you would like to migrate all of the IIS Personal stores to this store type, you can provide '*' instead of a comma separated list.| +|@store_path|NVARCHAR(15)|'My' or 'WebHosting'|My|Used to select which Windows Certificate store that holds the IIS bound certificate. 'My' for the personal store, 'WebHosting' for the web hosting store| +|@winrm_protocol|NVARCHAR(5)|'https' or 'http'|https|The protocol that WinRM will use for interacting with the certificate stores| +|@winrm_port|INT|1 - 65535|5986|The port that WinRM will use for interacting with the certificate stores| +|@spnwithport|NVARCHAR(5)|'true' or 'false'|false|If set to 'true,' the `-IncludePortInSPN` flag will be set when WinRM creates the remote PowerShell connection| + +## UpgradeIISPersonalToWinCert + +This script creates a 'WinCert' certificate store copy for a provided list of 'IIS Personal' certificate stores. It will also create a 'WinCert' version of every 'IIS Personal' certificate store container. + +**Notes** + +- This script does not delete the IIS certificate stores or containers. +- By default, the orchestrator will use its service account credentials to connect to the new certificate stores. If other credentials should be used instead, they should be configured for each store from the Command Certificate Stores page. + +This script accepts four parameters: + +| Parameter | Type | Valid Values | Default Value| Description | +|----|----|----|----|----| +|@comma_separated_store_ids|NVARCHAR(MAX)|* or a comma separated list of certificate store IDs. ex: 6A79C7A3-1A9B-413B-9C6E-571EA52B9E31,3929B040-299C-4EDF-98A4-EF0FFC21DAF9,C8A62749-10C3-4441-A0CC-AD83CA9051B5||A comma separated list of IDs of the IIS Personal certificate stores that you would like to migrate. If you would like to migrate all of the IIS Personal stores to this store type, you can provide '*' instead of a comma separated list.| +|@winrm_protocol|NVARCHAR(5)|'https' or 'http'|https|The protocol that WinRM will use for interacting with the certificate stores| +|@winrm_port|INT|1 - 65535|5986|The port that WinRM will use for interacting with the certificate stores| +|@spnwithport|NVARCHAR(5)|'true' or 'false'|false|If set to 'true,' the `-IncludePortInSPN` flag will be set when WinRM creates the remote PowerShell connection| + +
+ +
+ +Deletion Scripts + +## DeleteIISStores + +This script will delete all IIS Personal, IIS Roots, and IIS Revoked certificate store types, certificate stores, and certificate store containers. + +
+ +# Migration Order + +In order for a successful migration, the guides contained in this document should be completed in the following order: + +1. If you have any IIS Roots or IIS Revoked stores, follow this guide: ['IIS Roots' and 'IIS Revoked' Migration Guide](#iis-roots-and-iis-revoked-migration-guide) +1. If you have any IIS Personal stores, follow this guide: [IIS Personal Migration Guide](#iis-personal-migration-guide) +1. Finally, Follow the [Migration Finalization Guide](#migration-finalization-guide) + +## IIS Roots and IIS Revoked Migration Guide + +
+ +Expand for steps + +The 'IIS Roots' and 'IIS Revoked' certificate store types can only be migrated to the WinCert certificate store type. + +1. Execute the `CreateWinCertStoreType` SQL script to define the WinCert certificate store type if you have not already created this type. +1. Execute the `UpgradeIISRevokedAndRootsToWinCert` script to create WinCert store copies of your 'IIS Roots' and 'IIS Revoked' stores. See [UpgradeIISRevokedAndRootsToWinCert section](#upgradeiisrevokedandrootstowincert) for usage information. + +
+ +## IIS Personal Migration Guide + +The IIS Personal stores can either be migrated to the WinCert store type, the IISU store type, or both, depending on your environment configuration. If you would like to manage all of the certificates in a server's 'Personal' certificate store, you should migrate the store to the WinCert type. If you would like to manage an IIS bound certificate on the server, you should migrate to the IISU store type. + +### IIS Personal to WinCert Migration Guide + +
+ +Expand for steps + +1. If you have not already defined the WinCert store type, execute the `CreateWinCertStoreType` SQL script. +1. If you do not wish to create a WinCert store copy of all of your IIS Personal stores or have unique WinRM configurations for each certificate store, you should collect a list of IDs of the IIS Personal certificate stores that you wish to migrate at this time. +1. Execute the `UpgradeIISPersonalToWinCert` script to create WinCert store copies of your 'IIS Personal' stores. See [UpgradeIISPersonalToWinCert section](#upgradeiispersonaltowincert) for usage information. + +
+ +### IIS Personal to IISU Migration Guide + +
+ +Expand for steps + +1. If you have not already defined the IISU store type, execute the `CreateIISUCertStoreType` SQL script. +1. If you do not wish to create aN IISU store copy of all of your IIS Personal stores or have unique configurations for each certificate store, you should collect a list of IDs of the IIS Personal certificate stores that you wish to migrate at this time. +1. Execute the `UpgradeIISPersonalToIISU` script to create WinCert store copies of your 'IIS Personal' stores. See [UpgradeIISPersonalToIISU section](#upgradeiispersonaltoiisu) for usage information. + +
+ +## Migration Finalization Guide + +At this point, you should have an IISU or WinCert store copy of every legacy IIS store type and store container. Follow these steps to complete the migration: + +
+ +Expand for steps + +1. Review the certificate stores that were created in the Command portal. Ensure that there is a copy of each legacy IIS store with the desired store type (either IISU or WinCert). If desired, wait for any scheduled inventory jobs to run on the new stores and ensure that they return the expected certificates. +1. Review the certificate store containers that were created in the Command portal. If you upgraded IIS Personal stores to both WinCert and IISU store types, all IIS Personal certificate store containers will have an IISU and a WinCert store type copy (they will have names that end in either ' - Upgraded WinCert' or ' - Upgraded IISU'). You will need to determine if you would like to keep the IISU or the WinCert copy of each container set then delete the one that you do not wish to keep. If you would like to keep both, you must rename one of the two. This is necessary, as the deletion step will remove the ' - Upgraded ...' text from the end of the store names; without updating the names, execution of the deletion script will result in invalid stores with the same name. +1. Execute the `DeleteIISStores` script to remove all legacy IIS stores, store types, and containers. This script will also remove the ' - Upgraded ...' text from the migrated container names. + +
+ +At this point, all of your legacy IIS stores should be upgraded to either the IISU store type or the WinCert store type, and all legacy IIS stores, store types, containers, and jobs should be removed from Command. \ No newline at end of file diff --git a/Migration-Scripts/CreateIISUCertStoreType.sql b/Migration-Scripts/CreateIISUCertStoreType.sql new file mode 100644 index 0000000..c48a372 --- /dev/null +++ b/Migration-Scripts/CreateIISUCertStoreType.sql @@ -0,0 +1,454 @@ +SET NOCOUNT ON + +BEGIN TRY + BEGIN TRANSACTION + + DECLARE @registration_index AS INT; + SELECT @registration_index = MAX([ServerRegistration]) FROM [cms_agents].[CertStoreTypes]; + SET @registration_index = COALESCE(@registration_index + 1, 0); + + DECLARE @current_storetype_id AS INT; + SELECT @current_storetype_id = IDENT_CURRENT('[cms_agents].[CertStoreTypes]') + 1; + + DECLARE @enrollment_job_id uniqueidentifier = NEWID(); + DECLARE @inventory_job_id uniqueidentifier = NEWID(); + DECLARE @management_job_id uniqueidentifier = NEWID(); + + -- create enrollment job type + INSERT INTO [cms_agents].[JobTypes] ( + [Id] + ,[ConfigurationEndpoint] + ,[CompletionEndpoint] + ,[SubmitEndpoint] + ,[Name] + ,[Description] + ,[Capability] + ) + VALUES + ( + @enrollment_job_id, -- Id + 'AnyReenrollment/Configure', -- ConfigurationEndpoint + 'AnyReenrollment/Complete', -- CompletionEndpoint + 'AnyReenrollment/Submit', -- SubmitEndpoint + 'IISUReenrollment', -- Name + 'IIS Bound Certificate Reenrollment', -- Description + 'CertStores.IISU.Reenrollment' -- Capability + ); + + -- create inventory job type + INSERT INTO [cms_agents].[JobTypes] ( + [Id] + ,[ConfigurationEndpoint] + ,[CompletionEndpoint] + ,[SubmitEndpoint] + ,[Name] + ,[Description] + ,[Capability] + ) + VALUES + ( + @inventory_job_id, -- Id + 'AnyInventory/Configure', -- ConfigurationEndpoint + 'AnyInventory/Complete', -- CompletionEndpoint + NULL, -- SubmitEndpoint + 'IISUInventory', -- Name + 'IIS Bound Certificate Inventory', -- Description + 'CertStores.IISU.Inventory' -- Capability + ); + + -- create management job type + INSERT INTO [cms_agents].[JobTypes] ( + [Id] + ,[ConfigurationEndpoint] + ,[CompletionEndpoint] + ,[SubmitEndpoint] + ,[Name] + ,[Description] + ,[Capability] + ) + VALUES + ( + @management_job_id, -- Id + 'AnyManagement/Configure', -- ConfigurationEndpoint + 'AnyManagement/Complete', -- CompletionEndpoint + NULL, -- SubmitEndpoint + 'IISUManagement', -- Name + 'IIS Bound Certificate Management', -- Description + 'CertStores.IISU.Management' -- Capability + ); + + -- create IISU certificate store type + INSERT INTO [cms_agents].[CertStoreTypes] ( + [Name] + ,[ShortName] + ,[LocalStore] + ,[ServerRegistration] + ,[ImportType] + ,[InventoryJobType] + ,[ManagementJobType] + ,[AddSupported] + ,[RemoveSupported] + ,[CreateSupported] + ,[DiscoveryJobType] + ,[EnrollmentJobType] + ,[InventoryEndpoint] + ,[EntryPasswordSupported] + ,[StorePasswordRequired] + ,[PrivateKeyAllowed] + ,[StorePathType] + ,[CustomAliasAllowed] + ,[PowerShell] + ,[PasswordStyle] + ,[BlueprintAllowed] + ) + VALUES + ( + 'IIS Bound Certificate', -- Name + 'IISU', -- ShortName + 0, -- LocalStore + @registration_index, -- ServerRegistration + @current_storetype_id, -- ImportType + @inventory_job_id, -- InventoryJobType + @management_job_id, -- ManagementJobType + 1, -- AddSupported + 1, -- RemoveSupported + 0, -- CreateSupported + NULL, -- DiscoveryJobType + @enrollment_job_id, -- EnrollmentJobType + '/AnyInventory/Update', -- InventoryEndpoint + 0, -- EntryPasswordSupported + 0, -- StorePasswordRequired + 2, -- PrivateKeyAllowed + '["My","WebHosting"]', -- StorePathType + 0, -- CustomAliasAllowed + 0, -- PowerShell + 0, -- PasswordStyle + 0 -- BlueprintAllowed + ); + + -- create WinRm protocol property + INSERT INTO [cms_agents].[CertStoreTypeProperties] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[Required] + ,[DependsOn] + ,[DefaultValue] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'WinRm Protocol', -- Name + 'WinRm Protocol', -- DisplayName + 2, -- Type + 1, -- Required + NULL, -- DependsOn + 'https,http' -- DefaultValue + ); + + -- create WinRm port property + INSERT INTO [cms_agents].[CertStoreTypeProperties] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[Required] + ,[DependsOn] + ,[DefaultValue] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'WinRm Port', -- Name + 'WinRm Port', -- DisplayName + 0, -- Type + 1, -- Required + NULL, -- DependsOn + '5986' -- DefaultValue + ); + + -- create SPN With Port protocol property + INSERT INTO [cms_agents].[CertStoreTypeProperties] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[Required] + ,[DependsOn] + ,[DefaultValue] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'spnwithport', -- Name + 'SPN With Port', -- DisplayName + 1, -- Type + 0, -- Required + NULL, -- DependsOn + 'false' -- DefaultValue + ); + + -- create Server Username property + INSERT INTO [cms_agents].[CertStoreTypeProperties] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[Required] + ,[DependsOn] + ,[DefaultValue] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'ServerUsername', -- Name + 'Server Username', -- DisplayName + 3, -- Type + 0, -- Required + NULL, -- DependsOn + NULL -- DefaultValue + ); + + -- create Server Password property + INSERT INTO [cms_agents].[CertStoreTypeProperties] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[Required] + ,[DependsOn] + ,[DefaultValue] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'ServerPassword', -- Name + 'Server Password', -- DisplayName + 3, -- Type + 0, -- Required + NULL, -- DependsOn + NULL -- DefaultValue + ); + + -- create Use SSL property + INSERT INTO [cms_agents].[CertStoreTypeProperties] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[Required] + ,[DependsOn] + ,[DefaultValue] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'ServerUseSsl', -- Name + 'Use SSL', -- DisplayName + 1, -- Type + 1, -- Required + NULL, -- DependsOn + 'true' -- DefaultValue + ); + + + -- create IIS Site Name entry parameter + INSERT INTO [cms_agents].[CertStoreTypeEntryParameters] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[RequiredWhen] + ,[DependsOn] + ,[DefaultValue] + ,[Options] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'SiteName', -- Name + 'IIS Site Name', -- DisplayName + 0, -- Type + 14, -- RequiredWhen + NULL, -- DependsOn + 'Default Web Site', -- DefaultValue + NULL -- Options + ); + + -- create IP Address entry parameter + INSERT INTO [cms_agents].[CertStoreTypeEntryParameters] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[RequiredWhen] + ,[DependsOn] + ,[DefaultValue] + ,[Options] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'IPAddress', -- Name + 'IP Address', -- DisplayName + 0, -- Type + 14, -- RequiredWhen + NULL, -- DependsOn + '*', -- DefaultValue + NULL -- Options + ); + + -- create Port entry parameter + INSERT INTO [cms_agents].[CertStoreTypeEntryParameters] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[RequiredWhen] + ,[DependsOn] + ,[DefaultValue] + ,[Options] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'Port', -- Name + 'Port', -- DisplayName + 0, -- Type + 14, -- RequiredWhen + NULL, -- DependsOn + '443', -- DefaultValue + NULL -- Options + ); + + -- create Host Name entry parameter + INSERT INTO [cms_agents].[CertStoreTypeEntryParameters] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[RequiredWhen] + ,[DependsOn] + ,[DefaultValue] + ,[Options] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'HostName', -- Name + 'Host Name', -- DisplayName + 0, -- Type + 0, -- RequiredWhen + NULL, -- DependsOn + NULL, -- DefaultValue + NULL -- Options + ); + + -- create SNI Support entry parameter + INSERT INTO [cms_agents].[CertStoreTypeEntryParameters] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[RequiredWhen] + ,[DependsOn] + ,[DefaultValue] + ,[Options] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'SniFlag', -- Name + 'SNI Support', -- DisplayName + 2, -- Type + 14, -- RequiredWhen + NULL, -- DependsOn + '0 - No SNI', -- DefaultValue + '0 - No SNI,1 - SNI Enabled,2 - Non SNI Binding,3 - SNI Binding' -- Options + ); + + -- create Protocol entry parameter + INSERT INTO [cms_agents].[CertStoreTypeEntryParameters] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[RequiredWhen] + ,[DependsOn] + ,[DefaultValue] + ,[Options] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'Protocol', -- Name + 'Protocol', -- DisplayName + 2, -- Type + 14, -- RequiredWhen + NULL, -- DependsOn + 'https', -- DefaultValue + 'https' -- Options + ); + + -- create Crypto Provider Name entry parameter + INSERT INTO [cms_agents].[CertStoreTypeEntryParameters] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[RequiredWhen] + ,[DependsOn] + ,[DefaultValue] + ,[Options] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'ProviderName', -- Name + 'Crypto Provider Name', -- DisplayName + 0, -- Type + 0, -- RequiredWhen + NULL, -- DependsOn + NULL, -- DefaultValue + NULL -- Options + ); + + -- create SAN entry parameter + INSERT INTO [cms_agents].[CertStoreTypeEntryParameters] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[RequiredWhen] + ,[DependsOn] + ,[DefaultValue] + ,[Options] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'SAN', -- Name + 'SAN', -- DisplayName + 0, -- Type + 8, -- RequiredWhen + NULL, -- DependsOn + NULL, -- DefaultValue + NULL -- Options + ); + + COMMIT TRANSACTION; +END TRY + +BEGIN CATCH + IF (@@TRANCOUNT > 0) + BEGIN + ROLLBACK TRANSACTION; + END + + SELECT + ERROR_MESSAGE() AS ErrorMessage, + ERROR_SEVERITY() AS Severity, + ERROR_STATE() AS ErrorState; +END CATCH + diff --git a/Migration-Scripts/CreateWinCertStoreType.sql b/Migration-Scripts/CreateWinCertStoreType.sql new file mode 100644 index 0000000..21c01a3 --- /dev/null +++ b/Migration-Scripts/CreateWinCertStoreType.sql @@ -0,0 +1,314 @@ +SET NOCOUNT ON + +BEGIN TRY + BEGIN TRANSACTION + + DECLARE @registration_index AS INT; + SELECT @registration_index = MAX([ServerRegistration]) FROM [cms_agents].[CertStoreTypes]; + SET @registration_index = COALESCE(@registration_index + 1, 0); + + DECLARE @current_storetype_id AS INT; + SELECT @current_storetype_id = IDENT_CURRENT('[cms_agents].[CertStoreTypes]') + 1; + + DECLARE @enrollment_job_id uniqueidentifier = NEWID(); + DECLARE @inventory_job_id uniqueidentifier = NEWID(); + DECLARE @management_job_id uniqueidentifier = NEWID(); + + -- create enrollment job type + INSERT INTO [cms_agents].[JobTypes] ( + [Id] + ,[ConfigurationEndpoint] + ,[CompletionEndpoint] + ,[SubmitEndpoint] + ,[Name] + ,[Description] + ,[Capability] + ) + VALUES + ( + @enrollment_job_id, -- Id + 'AnyReenrollment/Configure', -- ConfigurationEndpoint + 'AnyReenrollment/Complete', -- CompletionEndpoint + 'AnyReenrollment/Submit', -- SubmitEndpoint + 'WinCertReenrollment', -- Name + 'Windows Certificate Reenrollment', -- Description + 'CertStores.WinCert.Reenrollment' -- Capability + ); + + -- create inventory job type + INSERT INTO [cms_agents].[JobTypes] ( + [Id] + ,[ConfigurationEndpoint] + ,[CompletionEndpoint] + ,[SubmitEndpoint] + ,[Name] + ,[Description] + ,[Capability] + ) + VALUES + ( + @inventory_job_id, -- Id + 'AnyInventory/Configure', -- ConfigurationEndpoint + 'AnyInventory/Complete', -- CompletionEndpoint + NULL, -- SubmitEndpoint + 'WinCertInventory', -- Name + 'Windows Certificate Inventory', -- Description + 'CertStores.WinCert.Inventory' -- Capability + ); + + -- create management job type + INSERT INTO [cms_agents].[JobTypes] ( + [Id] + ,[ConfigurationEndpoint] + ,[CompletionEndpoint] + ,[SubmitEndpoint] + ,[Name] + ,[Description] + ,[Capability] + ) + VALUES + ( + @management_job_id, -- Id + 'AnyManagement/Configure', -- ConfigurationEndpoint + 'AnyManagement/Complete', -- CompletionEndpoint + NULL, -- SubmitEndpoint + 'WinCertManagement', -- Name + 'Windows Certificate Management', -- Description + 'CertStores.WinCert.Management' -- Capability + ); + + -- create windows certificate store type + INSERT INTO [cms_agents].[CertStoreTypes] ( + [Name] + ,[ShortName] + ,[LocalStore] + ,[ServerRegistration] + ,[ImportType] + ,[InventoryJobType] + ,[ManagementJobType] + ,[AddSupported] + ,[RemoveSupported] + ,[CreateSupported] + ,[DiscoveryJobType] + ,[EnrollmentJobType] + ,[InventoryEndpoint] + ,[EntryPasswordSupported] + ,[StorePasswordRequired] + ,[PrivateKeyAllowed] + ,[StorePathType] + ,[CustomAliasAllowed] + ,[PowerShell] + ,[PasswordStyle] + ,[BlueprintAllowed] + ) + VALUES + ( + 'Windows Certificate', -- Name + 'WinCert', -- ShortName + 0, -- LocalStore + @registration_index, -- ServerRegistration + @current_storetype_id, -- ImportType + @inventory_job_id, -- InventoryJobType + @management_job_id, -- ManagementJobType + 1, -- AddSupported + 1, -- RemoveSupported + 0, -- CreateSupported + NULL, -- DiscoveryJobType + @enrollment_job_id, -- EnrollmentJobType + '/AnyInventory/Update', -- InventoryEndpoint + 0, -- EntryPasswordSupported + 0, -- StorePasswordRequired + 2, -- PrivateKeyAllowed + NULL, -- StorePathType + 0, -- CustomAliasAllowed + 0, -- PowerShell + 0, -- PasswordStyle + 0 -- BlueprintAllowed + ); + + -- create WinRm protocol property + INSERT INTO [cms_agents].[CertStoreTypeProperties] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[Required] + ,[DependsOn] + ,[DefaultValue] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'WinRm Protocol', -- Name + 'WinRm Protocol', -- DisplayName + 2, -- Type + 1, -- Required + NULL, -- DependsOn + 'https,http' -- DefaultValue + ); + + -- create WinRm port property + INSERT INTO [cms_agents].[CertStoreTypeProperties] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[Required] + ,[DependsOn] + ,[DefaultValue] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'WinRm Port', -- Name + 'WinRm Port', -- DisplayName + 0, -- Type + 1, -- Required + NULL, -- DependsOn + '5986' -- DefaultValue + ); + + -- create SPN With Port protocol property + INSERT INTO [cms_agents].[CertStoreTypeProperties] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[Required] + ,[DependsOn] + ,[DefaultValue] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'spnwithport', -- Name + 'SPN With Port', -- DisplayName + 1, -- Type + 0, -- Required + NULL, -- DependsOn + 'false' -- DefaultValue + ); + + -- create Server Username property + INSERT INTO [cms_agents].[CertStoreTypeProperties] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[Required] + ,[DependsOn] + ,[DefaultValue] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'ServerUsername', -- Name + 'Server Username', -- DisplayName + 3, -- Type + 0, -- Required + NULL, -- DependsOn + NULL -- DefaultValue + ); + + -- create Server Password property + INSERT INTO [cms_agents].[CertStoreTypeProperties] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[Required] + ,[DependsOn] + ,[DefaultValue] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'ServerPassword', -- Name + 'Server Password', -- DisplayName + 3, -- Type + 0, -- Required + NULL, -- DependsOn + NULL -- DefaultValue + ); + + -- create Use SSL property + INSERT INTO [cms_agents].[CertStoreTypeProperties] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[Required] + ,[DependsOn] + ,[DefaultValue] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'ServerUseSsl', -- Name + 'Use SSL', -- DisplayName + 1, -- Type + 1, -- Required + NULL, -- DependsOn + 'true' -- DefaultValue + ); + + -- create Crypto Provider Name entry parameter + INSERT INTO [cms_agents].[CertStoreTypeEntryParameters] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[RequiredWhen] + ,[DependsOn] + ,[DefaultValue] + ,[Options] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'ProviderName', -- Name + 'Crypto Provider Name', -- DisplayName + 0, -- Type + 0, -- RequiredWhen + NULL, -- DependsOn + NULL, -- DefaultValue + NULL -- Options + ); + + -- create SAN entry parameter + INSERT INTO [cms_agents].[CertStoreTypeEntryParameters] ( + [StoreTypeId] + ,[Name] + ,[DisplayName] + ,[Type] + ,[RequiredWhen] + ,[DependsOn] + ,[DefaultValue] + ,[Options] + ) + VALUES + ( + @current_storetype_id, -- StoreTypeId + 'SAN', -- Name + 'SAN', -- DisplayName + 0, -- Type + 8, -- RequiredWhen + NULL, -- DependsOn + NULL, -- DefaultValue + NULL -- Options + ); + + COMMIT TRANSACTION +END TRY + +BEGIN CATCH + IF (@@TRANCOUNT > 0) + BEGIN + ROLLBACK TRANSACTION; + END + + SELECT + ERROR_MESSAGE() AS ErrorMessage, + ERROR_SEVERITY() AS Severity, + ERROR_STATE() AS ErrorState; +END CATCH \ No newline at end of file diff --git a/Migration-Scripts/DeleteIISStores.sql b/Migration-Scripts/DeleteIISStores.sql new file mode 100644 index 0000000..f7a6438 --- /dev/null +++ b/Migration-Scripts/DeleteIISStores.sql @@ -0,0 +1,98 @@ +SET NOCOUNT ON + +BEGIN TRY + BEGIN TRANSACTION + + DECLARE @IISStoreTypeIds TABLE (Id int); + DECLARE @IISJobTypeIds TABLE (Id uniqueidentifier); + + INSERT INTO @IISStoreTypeIds + SELECT [StoreType] + FROM [cms_agents].[CertStoreTypes] + WHERE [ShortName] = 'IIS'; + + INSERT INTO @IISJobTypeIds + SELECT [Id] + FROM [cms_agents].[JobTypes] + WHERE [Capability] LIKE 'CertStores.IIS.%'; + + DELETE [csc] + FROM [cms_agents].[CertStoreCertificates] [csc] + INNER JOIN [cms_agents].[CertStoreInventoryItems] [csii] ON [csii].[Id] = [csc].[CertStoreInventoryItemId] + INNER JOIN [cms_agents].[CertStores] [cs] ON [csii].[CertStoreId] = [cs].[Id] + INNER JOIN @IISStoreTypeIds [sti] ON [sti].[Id] = [cs].[CertStoreType]; + + DELETE [csii] + FROM [cms_agents].[CertStoreInventoryItems] [csii] + INNER JOIN [cms_agents].[CertStores] [cs] ON [csii].[CertStoreId] = [cs].[Id] + INNER JOIN @IISStoreTypeIds [sti] ON [sti].[Id] = [cs].[CertStoreType]; + + DELETE [cs] + FROM [cms_agents].[CertStores] [cs] + INNER JOIN @IISStoreTypeIds [st] ON [cs].[CertStoreType] = [st].[Id]; + + DELETE [csc] + FROM [cms_agents].[CertStoreContainers] [csc] + INNER JOIN @IISStoreTypeIds [st] ON [csc].[CertStoreType] = [st].[Id]; + + DELETE [csij] + FROM [cms_agents].[CertStoreInventoryJobs] [csij] + INNER JOIN [cms_agents].[AgentSchedules] [as] ON [csij].[JobId] = [as].[JobId] + INNER JOIN @IISJobTypeIds [jti] ON [jti].[Id] = [as].[JobTypeId]; + + DELETE [csrj] + FROM [cms_agents].[CertStoreReenrollmentJobs] [csrj] + INNER JOIN [cms_agents].[AgentSchedules] [as] ON [csrj].[JobId] = [as].[JobId] + INNER JOIN @IISJobTypeIds [jti] ON [jti].[Id] = [as].[JobTypeId]; + + DELETE [csmj] + FROM [cms_agents].[CertStoreManagementJobs] [csmj] + INNER JOIN [cms_agents].[AgentSchedules] [as] ON [csmj].[JobId] = [as].[JobId] + INNER JOIN @IISJobTypeIds [jti] ON [jti].[Id] = [as].[JobTypeId]; + + DELETE [as] + FROM [cms_agents].[AgentSchedules] [as] + INNER JOIN @IISJobTypeIds [jt] ON [jt].[Id] = [as].[JobTypeId]; + + DELETE [cstp] + FROM [cms_agents].[CertStoreTypeProperties] [cstp] + INNER JOIN @IISStoreTypeIds [st] ON [cstp].[StoreTypeId] = [st].[id]; + + DELETE [cst] + FROM [cms_agents].[CertStoreTypes] [cst] + INNER JOIN @IISStoreTypeIds [st] ON [cst].[StoreType] = [st].[id]; + + DELETE [ars] + FROM [cms_agents].[AgentRegistrationSettings] [ars] + INNER JOIN @IISJobTypeIds [jt] ON [ars].[JobTypeId] = [jt].[Id]; + + DELETE [ac] + FROM [cms_agents].[AgentCapabilities] [ac] + INNER JOIN @IISJobTypeIds [jt] ON [ac].[JobTypeId] = [jt].[Id]; + + DELETE [jt] + FROM [cms_agents].[JobTypes] [jt] + INNER JOIN @IISJobTypeIds [jti] ON [jt].[Id] = [jti].[Id]; + + UPDATE [cms_agents].[CertStoreContainers] + SET [Name] = REPLACE([Name],' - Upgraded IISU','') + WHERE [Name] LIKE '% - Upgraded IISU'; + + UPDATE [cms_agents].[CertStoreContainers] + SET [Name] = REPLACE([Name],' - Upgraded WinCert','') + WHERE [Name] LIKE '% - Upgraded WinCert'; + + COMMIT TRANSACTION; +END TRY + +BEGIN CATCH + IF (@@TRANCOUNT > 0) + BEGIN + ROLLBACK TRANSACTION; + END + + SELECT + ERROR_MESSAGE() AS ErrorMessage, + ERROR_SEVERITY() AS Severity, + ERROR_STATE() AS ErrorState; +END CATCH \ No newline at end of file diff --git a/Migration-Scripts/UpgradeIISPersonalToIISU.sql b/Migration-Scripts/UpgradeIISPersonalToIISU.sql new file mode 100644 index 0000000..42679f9 --- /dev/null +++ b/Migration-Scripts/UpgradeIISPersonalToIISU.sql @@ -0,0 +1,231 @@ +-- REQUIREMENTS: +-- SQL Server 2016 or later + +-- PREREQUISITES: +-- 1) The IISU certificate store type must already be properly set up in Keyfactor Command. Run the CreateIISUCertStoreType script to do so. +-- 2) Make sure to back up the targeted Keyfactor Command database before running this + +-- *** BEGIN SET UP *** + +-- *** STEP 1 OF 5: Either enter '*' to migrate all IIS Personal stores, or enter a comma separated list of IDs of stores to convert. +-- Ex.: '25f80f46-fe0a-4ee6-b666-f601effd6847,22d0ca17-1739-4282-a85b-e59a3b431cdd' *** +DECLARE @comma_separated_store_ids NVARCHAR(MAX) = '*'; + +-- *** STEP 2 OF 5: select which Windows certificate store contains the IIS bound certificate. either 'My' for the personal store +-- or 'WebHosting' for the web hosting store. *** +DECLARE @store_path NVARCHAR(15) = 'My'; + +-- *** STEP 3 OF 5: select the WinRM protocol that the Orchestrator should use. either 'https' or 'http'. *** +DECLARE @winrm_protocol NVARCHAR(5) = 'https'; + +-- *** STEP 4 OF 5: select the port that WinRM should use. *** +DECLARE @winrm_port INT = 5986; + +-- *** STEP 5 OF 5: Select whether or not the -IncludePortInSPN flag will be set when WinRM creates the remote PowerShell connection. either 'true' or 'false' *** +DECLARE @spnwithport NVARCHAR(5) = 'false'; + +-- *** END SET UP *** + + +SET NOCOUNT ON + +BEGIN TRY + BEGIN TRANSACTION + + DECLARE @store_ids_to_convert TABLE ([Id] UNIQUEIDENTIFIER); + DECLARE @iis_store_type_id INT = 6; + + PRINT 'validating...'; + + SELECT @comma_separated_store_ids = REPLACE(@comma_separated_store_ids, ' ', ''); + + IF (@comma_separated_store_ids = '*') + BEGIN + INSERT INTO @store_ids_to_convert + SELECT [Id] FROM [cms_agents].[CertStores] [cs] + WHERE [cs].[CertStoreType] = @iis_store_type_id + END + ELSE + BEGIN + INSERT INTO @store_ids_to_convert + SELECT value FROM STRING_SPLIT(@comma_separated_store_ids, ','); + + DECLARE @invalid_ids TABLE ([InvalidId] UNIQUEIDENTIFIER); + + INSERT INTO @invalid_ids + SELECT [Id] FROM @store_ids_to_convert [si] + WHERE [si].[Id] NOT IN + ( + SELECT [Id] FROM [cms_agents].[CertStores] + ); + + IF ((SELECT COUNT(*) FROM @invalid_ids) > 0) + BEGIN + PRINT 'One or more invalid IIS Personal store ids provided. See output table for list of invalid identifiers.'; + SELECT * FROM @invalid_ids; + RETURN; + END + END + + IF (@store_path <> 'My' and @store_path <> 'WebHosting') + BEGIN + PRINT '@store_path must either be "My" or "WebHosting".'; + RETURN; + END + + IF (@winrm_protocol <> 'https' and @winrm_protocol <> 'http') + BEGIN + PRINT '@winrm_protocol must either be "https" or "http".'; + RETURN; + END + + IF (@spnwithport <> 'true' and @spnwithport <> 'false') + BEGIN + PRINT '@spnwithport must either be "true" or "false".'; + RETURN; + END + + PRINT 'Validation Complete'; + + DECLARE @iis_store_data TABLE ( + [IISInventoryJobId] UNIQUEIDENTIFIER, + [ContainerId] INT, + [ClientMachine] NVARCHAR(128), + [StoreApproved] BIT, + [AgentId] UNIQUEIDENTIFIER, + [InventorySchedule] NVARCHAR(512), + [InventoryEnabled] BIT, + [IISUInventoryJobId] UNIQUEIDENTIFIER, + [IISUStoreId] UNIQUEIDENTIFIER, + [IISUStoreContainerId] INT + ); + + -- aggregate necessary data for creating new certificate stores + INSERT INTO @iis_store_data SELECT + [cs].[CertStoreInventoryJobId] AS [IISInventoryJobId], + [cs].[ContainerId], + [cs].[ClientMachine], + [cs].[Approved] AS [StoreApproved], + [cs].[AgentId], + [as].[Schedule] AS [InventorySchedule], + [as].[Enabled] AS [InventoryEnabled], + CASE WHEN [cs].[CertStoreInventoryJobId] IS NOT NULL THEN NEWID() ELSE NULL END, + NEWID(), + NULL + FROM [cms_agents].[CertStores] [cs] + LEFT JOIN [cms_agents].[AgentSchedules] [as] ON [cs].[CertStoreInventoryJobId] = [as].[JobId] + INNER JOIN @store_ids_to_convert [si] ON [cs].[Id] = [si].[Id]; + + DECLARE @iisu_inventory_job_type_id UNIQUEIDENTIFIER; + SELECT @iisu_inventory_job_type_id = [Id] FROM [cms_agents].[JobTypes] WHERE [Name] = 'IISUInventory'; + + DECLARE @iisu_cert_store_type_id INT; + SELECT @iisu_cert_store_type_id = [StoreType] FROM [cms_agents].[CertStoreTypes] WHERE [ShortName] = 'IISU'; + + + -- create new certificate store containers if necessary + INSERT INTO [cms_agents].[CertStoreContainers] ( + [Name] + ,[Schedule] + ,[OverwriteSchedules] + ,[CertStoreType] + ) + SELECT + [csc].[Name] + ' - Upgraded IISU', + [csc].[Schedule], + [csc].[OverwriteSchedules], + @iisu_cert_store_type_id + FROM [cms_agents].[CertStoreContainers] [csc] + WHERE [csc].[CertStoreType] = @iis_store_type_id + AND [csc].[Id] NOT IN + ( + -- make sure we are only upgrading containers that haven't been upgraded yet + SELECT [csc].[Id] FROM [cms_agents].[CertStoreContainers] [csc2] + WHERE [csc].[Name] + ' - Upgraded IISU' = [csc2].[Name] + ); + + -- associate new certificate store containers with aggregated certificate store data + UPDATE @iis_store_data + SET [IISUStoreContainerId] = [wincsc].[Id] + FROM @iis_store_data [psd] + INNER JOIN [cms_agents].[CertStoreContainers] [csc] ON [psd].[ContainerId] = [csc].[Id] + INNER JOIN [cms_agents].[CertStoreContainers] [wincsc] ON [wincsc].[Name] = [csc].[Name] + ' - Upgraded IISU'; + + + -- create new agent schedules + INSERT INTO [cms_agents].[AgentSchedules] ( + [JobId] + ,[AgentId] + ,[JobTypeId] + ,[Schedule] + ,[Enabled] + ,[RequestTimestamp] + ,[Retries] + ) + SELECT + [psd].[IISUInventoryJobId], + [psd].[AgentId], + @iisu_inventory_job_type_id, + [psd].[InventorySchedule], + [psd].[InventoryEnabled], + NULL, + 0 + FROM @iis_store_data [psd] + WHERE [psd].[IISInventoryJobId] IS NOT NULL; + + + -- create new inventory jobs + INSERT INTO [cms_agents].[CertStoreInventoryJobs] ( + [JobId] + ,[InventoryEndpoint] + ,[RequestTimestamp] + ) + SELECT + [psd].[IISUInventoryJobId], + '/AnyInventory/Update', + CURRENT_TIMESTAMP + FROM @iis_store_data [psd] + WHERE [psd].[IISInventoryJobId] IS NOT NULL; + + + -- create new certificate stores + INSERT INTO [cms_agents].[CertStores] ( + [Id] + ,[CertStoreInventoryJobId] + ,[ContainerId] + ,[ClientMachine] + ,[StorePath] + ,[CertStoreType] + ,[Approved] + ,[CreateIfMissing] + ,[Properties] + ,[AgentId] + ) + SELECT + [psd].[IISUStoreId], + [psd].[IISUInventoryJobId], + [psd].[IISUStoreContainerId], + [psd].[ClientMachine], + @store_path, + @iisu_cert_store_type_id, + [psd].[StoreApproved], + 0, + '{"WinRm Protocol":"'+@winrm_protocol+'","WinRm Port":"'+CONVERT(NVARCHAR(10),@winrm_port)+'","spnwithport":"'+@spnwithport+'","ServerUsername":"'+CONVERT(NVARCHAR(36),NEWID())+'","ServerPassword":"'+CONVERT(NVARCHAR(36),NEWID())+'","ServerUseSsl":"true"}', + [psd].[AgentId] + FROM @iis_store_data [psd]; + + + COMMIT TRANSACTION; +END TRY + +BEGIN CATCH + IF (@@TRANCOUNT > 0) + BEGIN + ROLLBACK TRANSACTION; + END + + SELECT + ERROR_MESSAGE() AS ErrorMessage, + ERROR_SEVERITY() AS Severity, + ERROR_STATE() AS ErrorState; +END CATCH diff --git a/Migration-Scripts/UpgradeIISPersonalToWinCert.sql b/Migration-Scripts/UpgradeIISPersonalToWinCert.sql new file mode 100644 index 0000000..9ad194b --- /dev/null +++ b/Migration-Scripts/UpgradeIISPersonalToWinCert.sql @@ -0,0 +1,224 @@ +-- REQUIREMENTS: +-- SQL Server 2016 or later + +-- PREREQUISITES: +-- 1) The WinCert certificate store type must already be properly set up in Keyfactor Command. Run the CreateWinCertStoreType script to do so. +-- 2) Make sure to back up the targeted Keyfactor Command database before running this + +-- *** BEGIN SET UP *** + +-- *** STEP 1 OF 4: Either enter '*' to migrate all IIS Personal stores, or enter a comma separated list of IDs of stores to convert. +-- Ex.: '25f80f46-fe0a-4ee6-b666-f601effd6847,22d0ca17-1739-4282-a85b-e59a3b431cdd' *** +DECLARE @comma_separated_store_ids NVARCHAR(MAX) = '*'; + +-- *** STEP 2 OF 4: select the WinRM protocol that the Orchestrator should use. either 'https' or 'http'. *** +DECLARE @winrm_protocol NVARCHAR(5) = 'https'; + +-- *** STEP 3 OF 4: select the port that WinRM should use. *** +DECLARE @winrm_port INT = 5986; + +-- *** STEP 4 OF 4: Select whether or not the -IncludePortInSPN flag will be set when WinRM creates the remote PowerShell connection. either 'true' or 'false' *** +DECLARE @spnwithport NVARCHAR(5) = 'false'; + +-- *** END SET UP *** + + +SET NOCOUNT ON + +BEGIN TRY + BEGIN TRANSACTION + + DECLARE @store_ids_to_convert TABLE ([Id] UNIQUEIDENTIFIER); + DECLARE @iis_store_type_id INT = 6; + + PRINT 'validating parameters...'; + + SELECT @comma_separated_store_ids = REPLACE(@comma_separated_store_ids, ' ', ''); + + IF (@comma_separated_store_ids = '*') + BEGIN + INSERT INTO @store_ids_to_convert + SELECT [Id] FROM [cms_agents].[CertStores] [cs] + WHERE [cs].[CertStoreType] = @iis_store_type_id + END + ELSE + BEGIN + INSERT INTO @store_ids_to_convert + SELECT value FROM STRING_SPLIT(@comma_separated_store_ids, ','); + + DECLARE @invalid_ids TABLE ([InvalidId] UNIQUEIDENTIFIER); + + INSERT INTO @invalid_ids + SELECT [Id] FROM @store_ids_to_convert [si] + WHERE [si].[Id] NOT IN + ( + SELECT [Id] FROM [cms_agents].[CertStores] + ); + + IF ((SELECT COUNT(*) FROM @invalid_ids) > 0) + BEGIN + PRINT 'One or more invalid IIS Personal store ids provided. See output table for list of invalid identifiers.'; + SELECT * FROM @invalid_ids; + RETURN; + END + END + + IF (@winrm_protocol <> 'https' and @winrm_protocol <> 'http') + BEGIN + PRINT '@winrm_protocol must either be "https" or "http".'; + RETURN; + END + + IF (@spnwithport <> 'true' and @spnwithport <> 'false') + BEGIN + PRINT '@spnwithport must either be "true" or "false".'; + RETURN; + END + + PRINT 'Validation Complete'; + + DECLARE @store_path NVARCHAR(256) = 'My'; + + DECLARE @iis_store_data TABLE ( + [IISInventoryJobId] UNIQUEIDENTIFIER, + [ContainerId] INT, + [ClientMachine] NVARCHAR(128), + [StoreApproved] BIT, + [AgentId] UNIQUEIDENTIFIER, + [InventorySchedule] NVARCHAR(512), + [InventoryEnabled] BIT, + [WinInventoryJobId] UNIQUEIDENTIFIER, + [WinStoreId] UNIQUEIDENTIFIER, + [WinStoreContainerId] INT + ); + + -- aggregate necessary data for creating new certificate stores + INSERT INTO @iis_store_data SELECT + [cs].[CertStoreInventoryJobId] AS [IISInventoryJobId], + [cs].[ContainerId], + [cs].[ClientMachine], + [cs].[Approved] AS [StoreApproved], + [cs].[AgentId], + [as].[Schedule] AS [InventorySchedule], + [as].[Enabled] AS [InventoryEnabled], + CASE WHEN [cs].[CertStoreInventoryJobId] IS NOT NULL THEN NEWID() ELSE NULL END, + NEWID(), + NULL + FROM [cms_agents].[CertStores] [cs] + LEFT JOIN [cms_agents].[AgentSchedules] [as] ON [cs].[CertStoreInventoryJobId] = [as].[JobId] + INNER JOIN @store_ids_to_convert [si] ON [cs].[Id] = [si].[Id]; + + DECLARE @win_inventory_job_type_id UNIQUEIDENTIFIER; + SELECT @win_inventory_job_type_id = [Id] FROM [cms_agents].[JobTypes] WHERE [Name] = 'WinCertInventory'; + + DECLARE @win_cert_store_type_id INT; + SELECT @win_cert_store_type_id = [StoreType] FROM [cms_agents].[CertStoreTypes] WHERE [ShortName] = 'WinCert'; + + + -- create new certificate store containers if necessary + INSERT INTO [cms_agents].[CertStoreContainers] ( + [Name] + ,[Schedule] + ,[OverwriteSchedules] + ,[CertStoreType] + ) + SELECT + [csc].[Name] + ' - Upgraded WinCert', + [csc].[Schedule], + [csc].[OverwriteSchedules], + @win_cert_store_type_id + FROM [cms_agents].[CertStoreContainers] [csc] + WHERE [csc].[CertStoreType] = @iis_store_type_id + AND [csc].[Id] NOT IN + ( + -- make sure we are only upgrading containers that haven't been upgraded yet + SELECT [csc].[Id] FROM [cms_agents].[CertStoreContainers] [csc2] + WHERE [csc].[Name] + ' - Upgraded WinCert' = [csc2].[Name] + ); + + + + -- associate new certificate store containers with aggregated certificate store data + UPDATE @iis_store_data + SET [WinStoreContainerId] = [wincsc].[Id] + FROM @iis_store_data [psd] + INNER JOIN [cms_agents].[CertStoreContainers] [csc] ON [psd].[ContainerId] = [csc].[Id] + INNER JOIN [cms_agents].[CertStoreContainers] [wincsc] ON [wincsc].[Name] = [csc].[Name] + ' - Upgraded WinCert'; + + + -- create new agent schedules + INSERT INTO [cms_agents].[AgentSchedules] ( + [JobId] + ,[AgentId] + ,[JobTypeId] + ,[Schedule] + ,[Enabled] + ,[RequestTimestamp] + ,[Retries] + ) + SELECT + [psd].[WinInventoryJobId], + [psd].[AgentId], + @win_inventory_job_type_id, + [psd].[InventorySchedule], + [psd].[InventoryEnabled], + NULL, + 0 + FROM @iis_store_data [psd] + WHERE [psd].[IISInventoryJobId] IS NOT NULL; + + + -- create new inventory jobs + INSERT INTO [cms_agents].[CertStoreInventoryJobs] ( + [JobId] + ,[InventoryEndpoint] + ,[RequestTimestamp] + ) + SELECT + [psd].[WinInventoryJobId], + '/AnyInventory/Update', + CURRENT_TIMESTAMP + FROM @iis_store_data [psd] + WHERE [psd].[IISInventoryJobId] IS NOT NULL; + + + -- create new certificate stores + INSERT INTO [cms_agents].[CertStores] ( + [Id] + ,[CertStoreInventoryJobId] + ,[ContainerId] + ,[ClientMachine] + ,[StorePath] + ,[CertStoreType] + ,[Approved] + ,[CreateIfMissing] + ,[Properties] + ,[AgentId] + ) + SELECT + [psd].[WinStoreId], + [psd].[WinInventoryJobId], + [psd].[WinStoreContainerId], + [psd].[ClientMachine], + @store_path, + @win_cert_store_type_id, + [psd].[StoreApproved], + 0, + '{"WinRm Protocol":"'+@winrm_protocol+'","WinRm Port":"'+CONVERT(NVARCHAR(10),@winrm_port)+'","spnwithport":"'+@spnwithport+'","ServerUsername":"'+CONVERT(NVARCHAR(36),NEWID())+'","ServerPassword":"'+CONVERT(NVARCHAR(36),NEWID())+'","ServerUseSsl":"true"}', + [psd].[AgentId] + FROM @iis_store_data [psd]; + + COMMIT TRANSACTION; +END TRY + +BEGIN CATCH + IF (@@TRANCOUNT > 0) + BEGIN + ROLLBACK TRANSACTION; + END + + SELECT + ERROR_MESSAGE() AS ErrorMessage, + ERROR_SEVERITY() AS Severity, + ERROR_STATE() AS ErrorState; +END CATCH \ No newline at end of file diff --git a/Migration-Scripts/UpgradeIISRevokedAndRootsToWinCert.sql b/Migration-Scripts/UpgradeIISRevokedAndRootsToWinCert.sql new file mode 100644 index 0000000..af2f05d --- /dev/null +++ b/Migration-Scripts/UpgradeIISRevokedAndRootsToWinCert.sql @@ -0,0 +1,196 @@ +-- REQUIREMENTS: +-- SQL Server 2016 or later + +-- PREREQUISITES: +-- 1) The WinCert certificate store type must already be properly set up in Keyfactor Command. Run the CreateWinCertStoreType script to do so. +-- 2) Make sure to back up the targeted Keyfactor Command database before running this + +-- *** BEGIN SET UP *** + +-- *** STEP 1 OF 3: select the WinRM protocol that the Orchestrator should use. either 'https' or 'http'. *** +DECLARE @winrm_protocol NVARCHAR(5) = 'https'; + +-- *** STEP 2 OF 3: select the port that WinRM should use. *** +DECLARE @winrm_port INT = 5986; + +-- *** STEP 3 OF 3: Select whether or not the -IncludePortInSPN flag will be set when WinRM creates the remote PowerShell connection. either 'true' or 'false' *** +DECLARE @spnwithport NVARCHAR(5) = 'false'; + +-- *** END SET UP *** + + +SET NOCOUNT ON + +BEGIN TRY + BEGIN TRANSACTION + + PRINT 'validating parameters...'; + + IF (@winrm_protocol <> 'https' and @winrm_protocol <> 'http') + BEGIN + PRINT '@winrm_protocol must either be "https" or "http".'; + RETURN; + END + + IF (@spnwithport <> 'true' and @spnwithport <> 'false') + BEGIN + PRINT '@spnwithport must either be "true" or "false".'; + RETURN; + END + + PRINT 'Validation Complete'; + + DECLARE @upgrade_script NVARCHAR(MAX) = N' + + DECLARE @iis_store_data TABLE ( + [IISInventoryJobId] UNIQUEIDENTIFIER, + [ContainerId] INT, + [ClientMachine] NVARCHAR(128), + [StoreApproved] BIT, + [AgentId] UNIQUEIDENTIFIER, + [InventorySchedule] NVARCHAR(512), + [InventoryEnabled] BIT, + [WinInventoryJobId] UNIQUEIDENTIFIER, + [WinStoreId] UNIQUEIDENTIFIER, + [WinStoreContainerId] INT + ); + + -- aggregate necessary data for creating new certificate stores + INSERT INTO @iis_store_data SELECT + [cs].[CertStoreInventoryJobId] AS [IISInventoryJobId], + [cs].[ContainerId], + [cs].[ClientMachine], + [cs].[Approved] AS [StoreApproved], + [cs].[AgentId], + [as].[Schedule] AS [InventorySchedule], + [as].[Enabled] AS [InventoryEnabled], + CASE WHEN [cs].[CertStoreInventoryJobId] IS NOT NULL THEN NEWID() ELSE NULL END, + NEWID(), + NULL + FROM [cms_agents].[CertStores] [cs] + LEFT JOIN [cms_agents].[AgentSchedules] [as] + ON [cs].[CertStoreInventoryJobId] = [as].[JobId] + WHERE [cs].[CertStoreType] = @iis_store_type_id; + + DECLARE @win_inventory_job_type_id UNIQUEIDENTIFIER; + SELECT @win_inventory_job_type_id = [Id] FROM [cms_agents].[JobTypes] WHERE [Name] = ''WinCertInventory''; + + DECLARE @win_cert_store_type_id INT; + SELECT @win_cert_store_type_id = [StoreType] FROM [cms_agents].[CertStoreTypes] WHERE [ShortName] = ''WinCert''; + + + -- create new certificate store containers if necessary + INSERT INTO [cms_agents].[CertStoreContainers] ( + [Name] + ,[Schedule] + ,[OverwriteSchedules] + ,[CertStoreType] + ) + SELECT + [csc].[Name] + '' - Upgraded WinCert'', + [csc].[Schedule], + [csc].[OverwriteSchedules], + @win_cert_store_type_id + FROM [cms_agents].[CertStoreContainers] [csc] WHERE [csc].CertStoreType = @iis_store_type_id; + + -- associate new certificate store containers with aggregated certificate store data + UPDATE @iis_store_data + SET [WinStoreContainerId] = [wincsc].[Id] + FROM @iis_store_data [psd] + INNER JOIN [cms_agents].[CertStoreContainers] [csc] ON [psd].[ContainerId] = [csc].[Id] + INNER JOIN [cms_agents].[CertStoreContainers] [wincsc] ON [wincsc].[Name] = [csc].[Name] + '' - Upgraded WinCert''; + + + -- create new agent schedules + INSERT INTO [cms_agents].[AgentSchedules] ( + [JobId] + ,[AgentId] + ,[JobTypeId] + ,[Schedule] + ,[Enabled] + ,[RequestTimestamp] + ,[Retries] + ) + SELECT + [psd].[WinInventoryJobId], + [psd].[AgentId], + @win_inventory_job_type_id, + [psd].[InventorySchedule], + [psd].[InventoryEnabled], + NULL, + 0 + FROM @iis_store_data [psd] + WHERE [psd].[IISInventoryJobId] IS NOT NULL; + + + -- create new inventory jobs + INSERT INTO [cms_agents].[CertStoreInventoryJobs] ( + [JobId] + ,[InventoryEndpoint] + ,[RequestTimestamp] + ) + SELECT + [psd].[WinInventoryJobId], + ''/AnyInventory/Update'', + CURRENT_TIMESTAMP + FROM @iis_store_data [psd] + WHERE [psd].[IISInventoryJobId] IS NOT NULL; + + + -- create new certificate stores + INSERT INTO [cms_agents].[CertStores] ( + [Id] + ,[CertStoreInventoryJobId] + ,[ContainerId] + ,[ClientMachine] + ,[StorePath] + ,[CertStoreType] + ,[Approved] + ,[CreateIfMissing] + ,[Properties] + ,[AgentId] + ) + SELECT + [psd].[WinStoreId], + [psd].[WinInventoryJobId], + [psd].[WinStoreContainerId], + [psd].[ClientMachine], + @store_path, + @win_cert_store_type_id, + [psd].[StoreApproved], + 0, + ''{"WinRm Protocol":"''+@winrm_protocol+''","WinRm Port":"''+CONVERT(NVARCHAR(10),@winrm_port)+''","spnwithport":"''+@spnwithport+''","ServerUsername":"''+CONVERT(NVARCHAR(36),NEWID())+''","ServerPassword":"''+CONVERT(NVARCHAR(36),NEWID())+''","ServerUseSsl":"true"}'', + [psd].[AgentId] + FROM @iis_store_data [psd];' + + EXEC dbo.sp_executesql @upgrade_script, + N'@iis_store_type_id INT, + @store_path NVARCHAR(256), + @winrm_protocol NVARCHAR(5), + @winrm_port INT, + @spnwithport NVARCHAR(5)', + 4, 'Root', @winrm_protocol, @winrm_port, @spnwithport; + + EXEC dbo.sp_executesql @upgrade_script, + N'@iis_store_type_id INT, + @store_path NVARCHAR(256), + @winrm_protocol NVARCHAR(5), + @winrm_port INT, + @spnwithport NVARCHAR(5)', + 8, 'Disallowed', @winrm_protocol, @winrm_port, @spnwithport; + + + COMMIT TRANSACTION; +END TRY + +BEGIN CATCH + IF (@@TRANCOUNT > 0) + BEGIN + ROLLBACK TRANSACTION; + END + + SELECT + ERROR_MESSAGE() AS ErrorMessage, + ERROR_SEVERITY() AS Severity, + ERROR_STATE() AS ErrorState; +END CATCH From 85b63a65e1f585cef1f86d9be88138a3c9d53692 Mon Sep 17 00:00:00 2001 From: Matthew Dobrowsky Date: Fri, 28 Jun 2024 04:12:04 +0000 Subject: [PATCH 2/6] move migration files together --- Migration-Scripts/{ => Legacy-IIS}/CreateIISUCertStoreType.sql | 0 Migration-Scripts/{ => Legacy-IIS}/CreateWinCertStoreType.sql | 0 Migration-Scripts/{ => Legacy-IIS}/DeleteIISStores.sql | 0 .../Legacy-IIS/LegacyIISMigrationGuide.md | 0 Migration-Scripts/{ => Legacy-IIS}/UpgradeIISPersonalToIISU.sql | 0 .../{ => Legacy-IIS}/UpgradeIISPersonalToWinCert.sql | 0 .../{ => Legacy-IIS}/UpgradeIISRevokedAndRootsToWinCert.sql | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename Migration-Scripts/{ => Legacy-IIS}/CreateIISUCertStoreType.sql (100%) rename Migration-Scripts/{ => Legacy-IIS}/CreateWinCertStoreType.sql (100%) rename Migration-Scripts/{ => Legacy-IIS}/DeleteIISStores.sql (100%) rename LegacyIISMigrationGuide.md => Migration-Scripts/Legacy-IIS/LegacyIISMigrationGuide.md (100%) rename Migration-Scripts/{ => Legacy-IIS}/UpgradeIISPersonalToIISU.sql (100%) rename Migration-Scripts/{ => Legacy-IIS}/UpgradeIISPersonalToWinCert.sql (100%) rename Migration-Scripts/{ => Legacy-IIS}/UpgradeIISRevokedAndRootsToWinCert.sql (100%) diff --git a/Migration-Scripts/CreateIISUCertStoreType.sql b/Migration-Scripts/Legacy-IIS/CreateIISUCertStoreType.sql similarity index 100% rename from Migration-Scripts/CreateIISUCertStoreType.sql rename to Migration-Scripts/Legacy-IIS/CreateIISUCertStoreType.sql diff --git a/Migration-Scripts/CreateWinCertStoreType.sql b/Migration-Scripts/Legacy-IIS/CreateWinCertStoreType.sql similarity index 100% rename from Migration-Scripts/CreateWinCertStoreType.sql rename to Migration-Scripts/Legacy-IIS/CreateWinCertStoreType.sql diff --git a/Migration-Scripts/DeleteIISStores.sql b/Migration-Scripts/Legacy-IIS/DeleteIISStores.sql similarity index 100% rename from Migration-Scripts/DeleteIISStores.sql rename to Migration-Scripts/Legacy-IIS/DeleteIISStores.sql diff --git a/LegacyIISMigrationGuide.md b/Migration-Scripts/Legacy-IIS/LegacyIISMigrationGuide.md similarity index 100% rename from LegacyIISMigrationGuide.md rename to Migration-Scripts/Legacy-IIS/LegacyIISMigrationGuide.md diff --git a/Migration-Scripts/UpgradeIISPersonalToIISU.sql b/Migration-Scripts/Legacy-IIS/UpgradeIISPersonalToIISU.sql similarity index 100% rename from Migration-Scripts/UpgradeIISPersonalToIISU.sql rename to Migration-Scripts/Legacy-IIS/UpgradeIISPersonalToIISU.sql diff --git a/Migration-Scripts/UpgradeIISPersonalToWinCert.sql b/Migration-Scripts/Legacy-IIS/UpgradeIISPersonalToWinCert.sql similarity index 100% rename from Migration-Scripts/UpgradeIISPersonalToWinCert.sql rename to Migration-Scripts/Legacy-IIS/UpgradeIISPersonalToWinCert.sql diff --git a/Migration-Scripts/UpgradeIISRevokedAndRootsToWinCert.sql b/Migration-Scripts/Legacy-IIS/UpgradeIISRevokedAndRootsToWinCert.sql similarity index 100% rename from Migration-Scripts/UpgradeIISRevokedAndRootsToWinCert.sql rename to Migration-Scripts/Legacy-IIS/UpgradeIISRevokedAndRootsToWinCert.sql From 77ea00692b689424878a1b7eae9d2c0558684311 Mon Sep 17 00:00:00 2001 From: Matthew Dobrowsky Date: Thu, 11 Jul 2024 06:34:39 +0000 Subject: [PATCH 3/6] additional legacy migration notes --- .../Legacy-IIS/LegacyIISMigrationGuide.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Migration-Scripts/Legacy-IIS/LegacyIISMigrationGuide.md b/Migration-Scripts/Legacy-IIS/LegacyIISMigrationGuide.md index 84115a7..380e8ee 100644 --- a/Migration-Scripts/Legacy-IIS/LegacyIISMigrationGuide.md +++ b/Migration-Scripts/Legacy-IIS/LegacyIISMigrationGuide.md @@ -1,9 +1,12 @@ # Built-in IIS Certificate Store Type Migration Guide -As of Keyfactor Command v11, the built-in IIS certificate store types (IIS Personal, IIS Roots, and IIS Revoked) have been deprecated. This guide will instruct you on how to migrate the built-in IIS certificate store types to the open source iis-orchestrator certificate store types (IISU and WinCert) that can be found on the [Keyfactor GitHub page](https://github.com/Keyfactor/iis-orchestrator). +As of Keyfactor Command v11, the built-in IIS certificate store types (IIS Personal, IIS Roots, and IIS Revoked) have been deprecated. +Before upgrading to v11, if you have existing built-in IIS certificate stores they should be migrated to one of the new IIS store types. +This guide will instruct you on how to migrate the built-in IIS certificate store types to the open source iis-orchestrator certificate store types: IISU and WinCert. # Prerequisites +- Upgrade to Keyfactor Command >=10.4 (and < 11). This version or later is required to allow for the legacy IIS stores to be upgraded to both IISU and WinCert. - All orchestrators that are currently being used to orchestrate the built-in IIS store types must support the certificate store type that the built-in stores are being migrated to (IISU, WinCert, or both). - Ensure that you have a restorable database backup created before you begin the upgrade. @@ -15,12 +18,17 @@ There are six SQL scripts that are needed for this migration: Creation Scripts +These scripts can be used to create the Store Type definitions, if they do not already exist. +They may have already been created using `kfutil` or the Command portal. + ## CreateIISUCertStoreType +[CreateIISUCertStoreType.sql](./CreateIISUCertStoreType.sql) This script creates the IISU certificate store type. ## CreateWinCertStoreType +[CreateWinCertStoreType.sql](./CreateWinCertStoreType.sql) This script creates the WinCert certificate store type. @@ -31,6 +39,7 @@ This script creates the WinCert certificate store type. ## UpgradeIISRevokedAndRootsToWinCert +[UpgradeIISRevokedAndRootsToWinCert.sql](./UpgradeIISRevokedAndRootsToWinCert.sql) This script creates a 'WinCert' certificate store copy for every 'IIS Revoked' and 'IIS Roots' certificate store. It will also create a 'WinCert' version of every 'IIS Revoked' and 'IIS Roots' certificate store container. **Notes** @@ -48,6 +57,7 @@ This script accepts three parameters that allow configuration of WinRM: ## UpgradeIISPersonalToIISU +[UpgradeIISPersonalToIISU.sql](./UpgradeIISPersonalToIISU.sql) This script creates an 'IISU' certificate store copy for a provided list of 'IIS Personal' certificate stores. It will also create an 'IISU' version of every 'IIS Personal' certificate store container. **Notes** @@ -68,6 +78,7 @@ This script accepts four parameters: ## UpgradeIISPersonalToWinCert +[UpgradeIISPersonalToWinCert.sql](./UpgradeIISPersonalToWinCert.sql) This script creates a 'WinCert' certificate store copy for a provided list of 'IIS Personal' certificate stores. It will also create a 'WinCert' version of every 'IIS Personal' certificate store container. **Notes** @@ -92,6 +103,7 @@ This script accepts four parameters: ## DeleteIISStores +[DeleteIISStores.sql](./DeleteIISStores.sql) This script will delete all IIS Personal, IIS Roots, and IIS Revoked certificate store types, certificate stores, and certificate store containers. From 1e66b7d187b3bd1eed3cc6dedf48412369c3d927 Mon Sep 17 00:00:00 2001 From: Matthew Dobrowsky Date: Thu, 11 Jul 2024 06:55:16 +0000 Subject: [PATCH 4/6] add migration readme and readme-pre to link to migration --- .../Legacy-IIS/{LegacyIISMigrationGuide.md => readme.md} | 0 readme-src/readme-pre.md | 5 +++++ 2 files changed, 5 insertions(+) rename Migration-Scripts/Legacy-IIS/{LegacyIISMigrationGuide.md => readme.md} (100%) create mode 100644 readme-src/readme-pre.md diff --git a/Migration-Scripts/Legacy-IIS/LegacyIISMigrationGuide.md b/Migration-Scripts/Legacy-IIS/readme.md similarity index 100% rename from Migration-Scripts/Legacy-IIS/LegacyIISMigrationGuide.md rename to Migration-Scripts/Legacy-IIS/readme.md diff --git a/readme-src/readme-pre.md b/readme-src/readme-pre.md new file mode 100644 index 0000000..689bbfe --- /dev/null +++ b/readme-src/readme-pre.md @@ -0,0 +1,5 @@ +### Migrating Legacy IIS Stores +If you have existing IIS stores with the built-in IIS types, and plan to upgrade to Keyfactor Command 11 or later, you will need to migrate from the legacy store types to the IISU and WinCert store type defined in this repository. + +You can find the guide and migration scripts in this repository, located here: +[Legacy IIS Migration](./Migration-Scripts/Legacy-IIS) \ No newline at end of file From 21053b5f288f39ec71cb4efd93623b09f49cc8a3 Mon Sep 17 00:00:00 2001 From: Keyfactor Date: Thu, 11 Jul 2024 06:55:44 +0000 Subject: [PATCH 5/6] Update generated README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index b2cf34b..2dd6f6d 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,11 @@ WinCertStore Orchestrator is supported by Keyfactor for Keyfactor customers. If --- +### Migrating Legacy IIS Stores +If you have existing IIS stores with the built-in IIS types, and plan to upgrade to Keyfactor Command 11 or later, you will need to migrate from the legacy store types to the IISU and WinCert store type defined in this repository. +You can find the guide and migration scripts in this repository, located here: +[Legacy IIS Migration](./Migration-Scripts/Legacy-IIS) --- From 2e3cfe7963c576746aa20bb6f715c675448e03a1 Mon Sep 17 00:00:00 2001 From: Matthew Dobrowsky Date: Thu, 11 Jul 2024 15:17:53 +0000 Subject: [PATCH 6/6] update 2.4.3 changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d71d6f3..c798f14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +2.4.3 +* Adding Legacy IIS Migration scripting and Readme guide + 2.4.2 * Correct false positive error when completing an IIS inventory job. * Revert to specifying the version of PowerShell to use when establishing a local PowerShell Runspace.