Advertisement
loller5

Untitled

Jul 30th, 2024
389
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #Requires -modules SqlServer
  2.  
  3. <#
  4. .SYNOPSIS
  5.     Comprehensive Windows Server Update Services (WSUS) configuration and optimization script.
  6. .DESCRIPTION
  7.     Comprehensive Windows Server Update Services (WSUS) configuration and optimization script.
  8.     Features:
  9.         -Deep cleaning search and removal of unnecessary updates and drives by product title and update title.
  10.         -IIS Configuration validation and optimization.
  11.         -Disable device driver syncronization and caching.
  12.         -WSUS integrated update and computer cleanup
  13.         -Microsoft best practice WSUS database optimization and re-indexing
  14.         -Creation of daily and weekly optimization scheduled tasks.
  15.  
  16. .PARAMETER FirstRun
  17.     Presents a series of prompts for user to initiate all recommended first run optimization tasks. Additional parameters will be ignored, as they will be redundant.
  18.  
  19. .PARAMETER DeclineSupersededUpdates
  20. Declines all updates that have been approved and are superseded by other updates. The update will only be declined if a superseding update has been approved.
  21.  
  22. .PARAMETER DeepClean
  23.     Searches through most likely categories for unneeded updates and drivers to free up massive amounts of storage and improve database responsiveness. Prompts user to approve removal before deletion.
  24.  
  25. .PARAMETER DisableDrivers
  26.     Disable device driver syncronization and caching.
  27.  
  28. .PARAMETER CheckConfig
  29.     Validates current WSUS IIS configuration against recommended settings. Helps prevent frequent WSUS/IIS/SQL service crashes and the "RESET SERVER NODE" error.
  30.  
  31. .PARAMETER OptimizeServer
  32.     Runs all of Microsoft's built-in WSUS cleanup processes.
  33.  
  34. .PARAMETER OptimizeDatabase
  35.     Runs Microsoft's recommended SQL reindexing script.
  36.  
  37. .PARAMETER InstallDailyTask
  38.     Creates a scheduled task to run the OptimizeServer function nightly.
  39.  
  40. .PARAMETER InstallWeeklyTask
  41.     Creates a scheduled task to run the OptimizeDatabase function weekly.
  42.  
  43. .NOTES
  44.   Version:        1.2.1
  45.   Author:         Austin Warren
  46.   Creation Date:  2020/07/31
  47.  
  48. .EXAMPLE
  49.   Optimize-WsusServer.ps1 -FirstRun
  50.   Optimize-WsusServer.ps1 -DeepClean
  51.   Optimize-WsusServer.ps1 -InstallDailyTask -CheckConfig -OptimizeServer
  52. #>
  53.  
  54.  
  55. [CmdletBinding()]
  56. param (
  57.     [Parameter()]
  58.     [switch]
  59.     $FirstRun,
  60.     [Parameter()]
  61.     [switch]
  62.     $DisableDrivers,
  63.     [Parameter()]
  64.     [switch]
  65.     $DeepClean,
  66.     [Parameter()]
  67.     [switch]
  68.     $CheckConfig,
  69.     [Parameter()]
  70.     [switch]
  71.     $InstallDailyTask,
  72.     [Parameter()]
  73.     [switch]
  74.     $InstallWeeklyTask,
  75.     [Parameter()]
  76.     [switch]
  77.     $OptimizeServer,
  78.     [Parameter()]
  79.     [switch]
  80.     $OptimizeDatabase,
  81.     [switch]
  82.     $DeclineSupersededUpdates
  83. )
  84. #----------------------------------------------------------[Declarations]----------------------------------------------------------
  85.  
  86. # Recommended IIS settings: https://www.reddit.com/r/sysadmin/comments/996xul/getting_2016_updates_to_work_on_wsus/
  87. $recommendedIISSettings = @{
  88.     QueueLength              = 25000
  89.     LoadBalancerCapabilities = 'TcpLevel'
  90.     CpuResetInterval         = 15
  91.     RecyclingMemory          = 0
  92.     RecyclingPrivateMemory   = 0
  93.     ClientMaxRequestLength   = 204800
  94.     ClientExecutionTimeout   = 7200
  95. }
  96.  
  97. <#
  98. DeepClean
  99.  
  100. To find potentially unneeded updates:
  101.     1. WSUS management console
  102.     2. Updates > All Updates
  103.     3. Approval: Approved, Status: No Status
  104.     4. Look for unused products
  105.     5. Add titles to respective arrays below
  106.  
  107. Get-WsusProduct - Lists all Microsoft WSUS product categories.
  108. #>
  109.  
  110. # Common unneeded updates by ProductTitles
  111. $unneededUpdatesbyProductTitles = @(
  112.     "Security Only",
  113.     "Itanium",
  114.     "ia64",
  115.     "ARM64",
  116.     "Technical Preview",
  117.     "Insider Preview",
  118.     "Preview Of",
  119.     "Edge-Beta",
  120.     "Edge-Dev",
  121.     "Beta Channel",
  122.     "Prerelease",
  123.     "Preview",
  124.     "Windows 10 Version Next",
  125.     "Version 1507",
  126.     "Windows 10 Version 1511",
  127.     "Windows 10 Version 1607",
  128.     "Windows 10 Version 1703",
  129.     "Windows 10 Version 1709",
  130.     "Windows 10 Version 1803",
  131.     "Windows 10 Version 1809",
  132.     "Windows 10 Version 1903",
  133.     "Windows 10 Version 1909",
  134.     "ARM64-based",
  135.     "\(business editions\), version 1709",
  136.     "\(business editions\), version 1803",
  137.     "\(business editions\), version 1809",
  138.     "\(business editions\), version 1903",
  139.     "\(business editions\), version 1909",
  140.     "\(consumer editions\), version 1709",
  141.     "\(consumer editions\), version 1803",
  142.     "\(consumer editions\), version 1809",
  143.     "\(consumer editions\), version 1903",
  144.     "\(consumer editions\), version 1909",
  145.     "10 Education, version 1607",
  146.     "10 Education, version 1703",
  147.     "10 Education, version 1709",
  148.     "10 Education, version 1803",
  149.     "10 Education, version 1809",
  150.     "10 Education, version 1903",
  151.     "10 Education, version 1909",
  152.     "10 Education N, version 1607",
  153.     "10 Education N, version 1703",
  154.     "10 Education N, version 1709",
  155.     "10 Education N, version 1803",
  156.     "10 Education N, version 1809",
  157.     "10 Education N, version 1903",
  158.     "10 Education N, version 1909",
  159.     "10 Enterprise N, version 1607",
  160.     "10 Enterprise N, version 1703",
  161.     "10 Enterprise N, version 1709",
  162.     "10 Enterprise N, version 1803",
  163.     "10 Enterprise N, version 1809",
  164.     "10 Enterprise N, version 1903",
  165.     "10 Enterprise N, version 1909",
  166.     "10 Enterprise, version 1607",
  167.     "10 Enterprise, version 1703",
  168.     "10 Enterprise, version 1709",
  169.     "10 Enterprise, version 1803",
  170.     "10 Enterprise, version 1809",
  171.     "10 Enterprise, version 1903",
  172.     "10 Enterprise, version 1909",
  173.     "10 Pro, version 1607",
  174.     "10 Pro, version 1703",
  175.     "10 Pro, version 1709",
  176.     "10 Pro, version 1803",
  177.     "10 Pro, version 1809",
  178.     "10 Pro, version 1903",
  179.     "10 Pro, version 1909",
  180.     "10 Pro N, version 1607",
  181.     "10 Pro N, version 1703",
  182.     "10 Pro N, version 1709",
  183.     "10 Pro N, version 1803",
  184.     "10 Pro N, version 1809",
  185.     "10 Pro N, version 1903",
  186.     "10 Pro N, version 1909",
  187.     "10 Team, version 1607",
  188.     "10 Team, version 1703",
  189.     "10 Team, version 1709",
  190.     "10 Team, version 1803",
  191.     "10 Team, version 1809",
  192.     "10 Team, version 1903",
  193.     "10 Team, version 1909",
  194.     "Windows Server Next"
  195. )
  196.  
  197. # Common unneeded updates by Title
  198. $unneededUpdatesbyTitle = @(
  199.     "Security Only",
  200.     "Itanium",
  201.     "ia64",
  202.     "ARM64",
  203.     "Technical Preview",
  204.     "Insider Preview",
  205.     "Preview Of",
  206.     "Edge-Beta",
  207.     "Edge-Dev",
  208.     "Beta Channel",
  209.     "Prerelease",
  210.     "Preview",
  211.     "Windows 10 Version Next",
  212.     "Version 1507",
  213.     "Windows 10 Version 1511",
  214.     "Windows 10 Version 1607",
  215.     "Windows 10 Version 1703",
  216.     "Windows 10 Version 1709",
  217.     "Windows 10 Version 1803",
  218.     "Windows 10 Version 1809",
  219.     "Windows 10 Version 1903",
  220.     "Windows 10 Version 1909",
  221.     "ARM64-based",
  222.     "\(business editions\), version 1709",
  223.     "\(business editions\), version 1803",
  224.     "\(business editions\), version 1809",
  225.     "\(business editions\), version 1903",
  226.     "\(business editions\), version 1909",
  227.     "\(consumer editions\), version 1709",
  228.     "\(consumer editions\), version 1803",
  229.     "\(consumer editions\), version 1809",
  230.     "\(consumer editions\), version 1903",
  231.     "\(consumer editions\), version 1909",
  232.     "10 Education, version 1607",
  233.     "10 Education, version 1703",
  234.     "10 Education, version 1709",
  235.     "10 Education, version 1803",
  236.     "10 Education, version 1809",
  237.     "10 Education, version 1903",
  238.     "10 Education, version 1909",
  239.     "10 Education N, version 1607",
  240.     "10 Education N, version 1703",
  241.     "10 Education N, version 1709",
  242.     "10 Education N, version 1803",
  243.     "10 Education N, version 1809",
  244.     "10 Education N, version 1903",
  245.     "10 Education N, version 1909",
  246.     "10 Enterprise N, version 1607",
  247.     "10 Enterprise N, version 1703",
  248.     "10 Enterprise N, version 1709",
  249.     "10 Enterprise N, version 1803",
  250.     "10 Enterprise N, version 1809",
  251.     "10 Enterprise N, version 1903",
  252.     "10 Enterprise N, version 1909",
  253.     "10 Enterprise, version 1607",
  254.     "10 Enterprise, version 1703",
  255.     "10 Enterprise, version 1709",
  256.     "10 Enterprise, version 1803",
  257.     "10 Enterprise, version 1809",
  258.     "10 Enterprise, version 1903",
  259.     "10 Enterprise, version 1909",
  260.     "10 Pro, version 1607",
  261.     "10 Pro, version 1703",
  262.     "10 Pro, version 1709",
  263.     "10 Pro, version 1803",
  264.     "10 Pro, version 1809",
  265.     "10 Pro, version 1903",
  266.     "10 Pro, version 1909",
  267.     "10 Pro N, version 1607",
  268.     "10 Pro N, version 1703",
  269.     "10 Pro N, version 1709",
  270.     "10 Pro N, version 1803",
  271.     "10 Pro N, version 1809",
  272.     "10 Pro N, version 1903",
  273.     "10 Pro N, version 1909",
  274.     "10 Team, version 1607",
  275.     "10 Team, version 1703",
  276.     "10 Team, version 1709",
  277.     "10 Team, version 1803",
  278.     "10 Team, version 1809",
  279.     "10 Team, version 1903",
  280.     "10 Team, version 1909",
  281.     "Windows Server Next"
  282. )
  283.  
  284. <#
  285. REFERENCES
  286.     The complete guide to Microsoft WSUS and Configuration Manager SUP maintenance
  287.     https://support.microsoft.com/en-us/help/4490644/complete-guide-to-microsoft-wsus-and-configuration-manager-sup-maint
  288.  
  289.     Invoke-WsusServerCleanup
  290.     https://docs.microsoft.com/en-us/powershell/module/wsus/Invoke-WsusServerCleanup?view=win10-ps
  291.  
  292.     Reindex the WSUS Database
  293.     https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd939795(v=ws.10)
  294.  
  295.     Invoke-Sqlcmd
  296.     https://docs.microsoft.com/en-us/powershell/module/sqlserver/invoke-sqlcmd?view=sqlserver-ps
  297.  
  298.     How to Check if an Index Exists on a Table in SQL Server
  299.     https://littlekendra.com/2016/01/28/how-to-check-if-an-index-exists-on-a-table-in-sql-server/
  300. #>
  301.  
  302. <#
  303.     "[U]sed to create custom indexes in the SUSDB database. This is a one-time process, which is optional but recommended, as doing so will greatly improve performance during subsequent cleanup operations."
  304.     Modified to check if indexes already exist before creating them.
  305. #>
  306. $createCustomIndexesSQLQuery = @"
  307. USE [SUSDB]
  308. IF 0 = (SELECT COUNT(*) as index_count
  309.    FROM sys.indexes
  310.    WHERE object_id = OBJECT_ID('[dbo].[tbLocalizedPropertyForRevision]')
  311.    AND name='nclLocalizedPropertyID')
  312. BEGIN
  313. -- Create custom index in tbLocalizedPropertyForRevision
  314.     CREATE NONCLUSTERED INDEX [nclLocalizedPropertyID] ON [dbo].[tbLocalizedPropertyForRevision]
  315.     (
  316.          [LocalizedPropertyID] ASC
  317.     )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
  318. END
  319. ELSE
  320. BEGIN
  321.     PRINT '[nclLocalizedPropertyID] ON [dbo].[tbLocalizedPropertyForRevision] already exists'
  322. END ;
  323. GO
  324. IF 0 = (SELECT COUNT(*) as index_count
  325.    FROM sys.indexes
  326.    WHERE object_id = OBJECT_ID('[dbo].[tbRevisionSupersedesUpdate]')
  327.    AND name='nclSupercededUpdateID')
  328. BEGIN
  329. -- Create custom index in tbRevisionSupersedesUpdate
  330.     CREATE NONCLUSTERED INDEX [nclSupercededUpdateID] ON [dbo].[tbRevisionSupersedesUpdate]
  331.     (
  332.          [SupersededUpdateID] ASC
  333.     )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY];
  334. END
  335. ELSE
  336. BEGIN
  337.     PRINT '[nclSupercededUpdateID] ON [dbo].[tbRevisionSupersedesUpdate] already exists'
  338. END ;
  339. GO
  340. "@
  341.  
  342. <#
  343.     Microsoft recommended database maintenance script
  344.  
  345.     "The performance of large Windows Server Update Services (WSUS) deployments will degrade over time if the WSUS database
  346.     is not maintained properly. The WSUSDBMaintenance script is a T-SQL script that can be run by SQL Server administrators
  347.     to re-index and defragment WSUS databases. It should not be used on WSUS 2.0 databases.This script contributed by the
  348.     Microsoft WSUS team."
  349.  
  350.     Reference: https://support.microsoft.com/en-us/help/4490644/complete-guide-to-microsoft-wsus-and-configuration-manager-sup-maint
  351. #>
  352. $wsusDBMaintenanceSQLQuery = @"
  353. /******************************************************************************
  354. This sample T-SQL script performs basic maintenance tasks on SUSDB
  355. 1. Identifies indexes that are fragmented and defragments them. For certain
  356.   tables, a fill-factor is set in order to improve insert performance.
  357.   Based on MSDN sample at http://msdn2.microsoft.com/en-us/library/ms188917.aspx
  358.   and tailored for SUSDB requirements
  359. 2. Updates potentially out-of-date table statistics.
  360. ******************************************************************************/
  361.  
  362. USE SUSDB;
  363. GO
  364. SET NOCOUNT ON;
  365.  
  366. -- Rebuild or reorganize indexes based on their fragmentation levels
  367. DECLARE @work_to_do TABLE (
  368.    objectid int
  369.    , indexid int
  370.    , pagedensity float
  371.    , fragmentation float
  372.    , numrows int
  373. )
  374.  
  375. DECLARE @objectid int;
  376. DECLARE @indexid int;
  377. DECLARE @schemaname nvarchar(130);
  378. DECLARE @objectname nvarchar(130);
  379. DECLARE @indexname nvarchar(130);
  380. DECLARE @numrows int
  381. DECLARE @density float;
  382. DECLARE @fragmentation float;
  383. DECLARE @command nvarchar(4000);
  384. DECLARE @fillfactorset bit
  385. DECLARE @numpages int
  386.  
  387. -- Select indexes that need to be defragmented based on the following
  388. -- * Page density is low
  389. -- * External fragmentation is high in relation to index size
  390. PRINT 'Estimating fragmentation: Begin. ' + convert(nvarchar, getdate(), 121)
  391. INSERT @work_to_do
  392. SELECT
  393.    f.object_id
  394.    , index_id
  395.    , avg_page_space_used_in_percent
  396.    , avg_fragmentation_in_percent
  397.    , record_count
  398. FROM
  399.    sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL , NULL, 'SAMPLED') AS f
  400. WHERE
  401.    (f.avg_page_space_used_in_percent < 85.0 and f.avg_page_space_used_in_percent/100.0 * page_count < page_count - 1)
  402.    or (f.page_count > 50 and f.avg_fragmentation_in_percent > 15.0)
  403.    or (f.page_count > 10 and f.avg_fragmentation_in_percent > 80.0)
  404.  
  405. PRINT 'Number of indexes to rebuild: ' + cast(@@ROWCOUNT as nvarchar(20))
  406.  
  407. PRINT 'Estimating fragmentation: End. ' + convert(nvarchar, getdate(), 121)
  408.  
  409. SELECT @numpages = sum(ps.used_page_count)
  410. FROM
  411.    @work_to_do AS fi
  412.    INNER JOIN sys.indexes AS i ON fi.objectid = i.object_id and fi.indexid = i.index_id
  413.    INNER JOIN sys.dm_db_partition_stats AS ps on i.object_id = ps.object_id and i.index_id = ps.index_id
  414.  
  415. -- Declare the cursor for the list of indexes to be processed.
  416. DECLARE curIndexes CURSOR FOR SELECT * FROM @work_to_do
  417.  
  418. -- Open the cursor.
  419. OPEN curIndexes
  420.  
  421. -- Loop through the indexes
  422. WHILE (1=1)
  423. BEGIN
  424.    FETCH NEXT FROM curIndexes
  425.    INTO @objectid, @indexid, @density, @fragmentation, @numrows;
  426.    IF @@FETCH_STATUS < 0 BREAK;
  427.  
  428.    SELECT
  429.        @objectname = QUOTENAME(o.name)
  430.        , @schemaname = QUOTENAME(s.name)
  431.    FROM
  432.        sys.objects AS o
  433.        INNER JOIN sys.schemas as s ON s.schema_id = o.schema_id
  434.    WHERE
  435.        o.object_id = @objectid;
  436.  
  437.    SELECT
  438.        @indexname = QUOTENAME(name)
  439.        , @fillfactorset = CASE fill_factor WHEN 0 THEN 0 ELSE 1 END
  440.    FROM
  441.        sys.indexes
  442.    WHERE
  443.        object_id = @objectid AND index_id = @indexid;
  444.  
  445.    IF ((@density BETWEEN 75.0 AND 85.0) AND @fillfactorset = 1) OR (@fragmentation < 30.0)
  446.        SET @command = N'ALTER INDEX ' + @indexname + N' ON ' + @schemaname + N'.' + @objectname + N' REORGANIZE';
  447.    ELSE IF @numrows >= 5000 AND @fillfactorset = 0
  448.        SET @command = N'ALTER INDEX ' + @indexname + N' ON ' + @schemaname + N'.' + @objectname + N' REBUILD WITH (FILLFACTOR = 90)';
  449.    ELSE
  450.        SET @command = N'ALTER INDEX ' + @indexname + N' ON ' + @schemaname + N'.' + @objectname + N' REBUILD';
  451.    PRINT convert(nvarchar, getdate(), 121) + N' Executing: ' + @command;
  452.    EXEC (@command);
  453.    PRINT convert(nvarchar, getdate(), 121) + N' Done.';
  454. END
  455.  
  456. -- Close and deallocate the cursor.
  457. CLOSE curIndexes;
  458. DEALLOCATE curIndexes;
  459.  
  460. IF EXISTS (SELECT * FROM @work_to_do)
  461. BEGIN
  462.    PRINT 'Estimated number of pages in fragmented indexes: ' + cast(@numpages as nvarchar(20))
  463.    SELECT @numpages = @numpages - sum(ps.used_page_count)
  464.    FROM
  465.        @work_to_do AS fi
  466.        INNER JOIN sys.indexes AS i ON fi.objectid = i.object_id and fi.indexid = i.index_id
  467.        INNER JOIN sys.dm_db_partition_stats AS ps on i.object_id = ps.object_id and i.index_id = ps.index_id
  468.  
  469.    PRINT 'Estimated number of pages freed: ' + cast(@numpages as nvarchar(20))
  470. END
  471. GO
  472.  
  473. --Update all statistics
  474. PRINT 'Updating all statistics.' + convert(nvarchar, getdate(), 121)
  475. EXEC sp_updatestats
  476. PRINT 'Done updating statistics.' + convert(nvarchar, getdate(), 121)
  477. GO
  478. "@
  479.  
  480. #-----------------------------------------------------------[Functions]------------------------------------------------------------
  481.  
  482. function Confirm-Prompt ($prompt) {
  483.     <#
  484.     .SYNOPSIS
  485.     Y/N confirmation prompt.
  486.  
  487.     .DESCRIPTION
  488.     Displays Y/N confirmation prompt and returns true or false.
  489.  
  490.     .PARAMETER prompt
  491.     String displayed as prompt
  492.  
  493.     .EXAMPLE
  494.     Confirm-Prompt "Is this a question?"
  495.     #>
  496.     Write-Host "$prompt Y/N: " -BackgroundColor Blue -ForegroundColor White -NoNewline
  497.     $confirm = Read-Host
  498.  
  499.     if ($confirm.ToLower() -eq 'y') {
  500.         return $true
  501.     }
  502.     else {
  503.         return $false
  504.     }
  505. }
  506.  
  507. function Optimize-WsusUpdates {
  508.     <#
  509.     .SYNOPSIS
  510.     Runs all built-in WSUS cleanup processes.
  511.  
  512.     .DESCRIPTION
  513.     Runs all built-in WSUS cleanup processes.
  514.  
  515.     .LINK
  516.     https://docs.microsoft.com/en-us/powershell/scripting/developer/help/examples-of-comment-based-help?view=powershell-7
  517.     #>
  518.  
  519.     Write-Host "Deleting obsolete computers from WSUS database"
  520.     Invoke-WsusServerCleanup -CleanupObsoleteComputers
  521.  
  522.     Write-Host "Deleting obsolete updates"
  523.     Invoke-WsusServerCleanup -CleanupObsoleteUpdates
  524.  
  525.     Write-Host "Deleting unneeded content files"
  526.     Invoke-WsusServerCleanup -CleanupUnneededContentFiles
  527.  
  528.     Write-Host "Deleting obsolete update revisions"
  529.     Invoke-WsusServerCleanup -CompressUpdates
  530.  
  531.     Write-Host "Declining expired updates"
  532.     Invoke-WsusServerCleanup -DeclineExpiredUpdates
  533.  
  534.     Write-Host "Declining superceded updates"
  535.     Invoke-WsusServerCleanup -DeclineSupersededUpdates
  536.  
  537.     Write-Host "Declining additional superceded updates"
  538.     Decline-SupersededUpdates $TRUE
  539. }
  540.  
  541. function Optimize-WsusDatabase {
  542.     <#
  543.     .SYNOPSIS
  544.     Runs WSUS database optimization.
  545.  
  546.     .DESCRIPTION
  547.     Runs Microsoft's recommended WSUS database optimization.
  548.  
  549.     .LINK
  550.     https://support.microsoft.com/en-us/help/4490644/complete-guide-to-microsoft-wsus-and-configuration-manager-sup-maint
  551.  
  552.     .LINK
  553.     https://devblogs.microsoft.com/scripting/10-tips-for-the-sql-server-powershell-scripter/
  554.     #>
  555.  
  556.     # Check registry for WSUS database install type (SQL or WID)
  557.     $wsusSqlServerName = (get-itemproperty "HKLM:\Software\Microsoft\Update Services\Server\Setup" -Name "SqlServername").SqlServername
  558.  
  559.     # Set the named pipe to use based on WSUS db type
  560.     switch -Regex ($wsusSqlServerName) {
  561.         'SQLEXPRESS' { $serverInstance = 'np:\\.\pipe\MSSQL$SQLEXPRESS\sql\query'; break }
  562.         '##WID' { $serverInstance = 'np:\\.\pipe\MICROSOFT##WID\tsql\query'; break }
  563.         '##SSEE' { $serverInstance = 'np:\\.\pipe\MSSQL$MICROSOFT##SSEE\sql\query'; break }
  564.         default { $serverInstance = $wsusSqlServerName }
  565.     }
  566.  
  567.     # Setting query timeout value because both of these scripts are prone to timeout
  568.     # https://devblogs.microsoft.com/scripting/10-tips-for-the-sql-server-powershell-scripter/
  569.  
  570.     Write-Host "Creating custom indexes in WSUS index if they don't already exist. This will speed up future database optimizations."
  571.     #Create custom indexes in the database if they don't already exist
  572.     Invoke-Sqlcmd -query $createCustomIndexesSQLQuery -ServerInstance $serverInstance -QueryTimeout 120 -EncryptConnection:$false
  573.  
  574.     Write-Host "Running WSUS SQL database maintenence script. This can take an extremely long time on the first run."
  575.     #Run the WSUS SQL database maintenance script
  576.     Invoke-Sqlcmd -query $wsusDBMaintenanceSQLQuery -ServerInstance $serverInstance -QueryTimeout 40000 -EncryptConnection:$false
  577. }
  578.  
  579. function New-WsusMaintainenceTask($interval) {
  580.     <#
  581.     .SYNOPSIS
  582.     Creates a new WSUS optimization scheduled tasks.
  583.  
  584.     .DESCRIPTION
  585.     Creates or overwrites daily or weekly scheduled tasks for WSUS update and database optimization.
  586.  
  587.     .PARAMETER interval
  588.     Specifies "Daily" or "Weekly" tasks
  589.  
  590.     .LINK
  591.     https://docs.microsoft.com/en-us/powershell/module/scheduledtasks/?view=win10-ps
  592.     #>
  593.  
  594.     $taskName = "Optimize WSUS Server ($interval)"
  595.     $scriptPath = 'C:\Scripts'
  596.  
  597.     # Delete scheduled task with the same name if it already exists
  598.     If (Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue) {
  599.         Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
  600.         Write-Host "Unregistered Schedule Task: $taskName"
  601.     }
  602.  
  603.     # Change scheduled action based on Daily or Weekly
  604.     switch ($interval) {
  605.         'Daily' {
  606.             $trigger = New-ScheduledTaskTrigger -Daily -At "12pm"
  607.             $scriptAction = "-OptimizeServer"
  608.             Break
  609.         }
  610.         'Weekly' {
  611.             $trigger = New-ScheduledTaskTrigger -Weekly -At "2am" -DaysOfWeek Sunday
  612.             $scriptAction = "-OptimizeDatabase"
  613.             Break
  614.         }
  615.         Default {}
  616.     }
  617.  
  618.     $scriptName = Split-Path $MyInvocation.PSCommandPath -Leaf
  619.  
  620.     #Create "C:\Scripts" to store PS script
  621.     $null = New-Item -Path "$scriptPath" -ItemType Directory -Force
  622.     Write-Host "Created Directory: $scriptPath"
  623.  
  624.     # Copy current script to script
  625.     Copy-Item -Path $PSCommandPath -Destination $scriptPath -Force
  626.     Write-Host "Copied Script: $scriptName"
  627.  
  628.     # Create and register the scheduled task
  629.     $task = New-ScheduledTaskAction `
  630.         -Execute "powershell.exe" `
  631.         -Argument "-Command `"&'$($scriptPath)`\$($scriptName)'$scriptAction`""
  632.  
  633.     $settings = New-ScheduledTaskSettingsSet
  634.     $principal = New-ScheduledTaskPrincipal `
  635.         -UserId "NT AUTHORITY\SYSTEM" `
  636.         -LogonType ServiceAccount `
  637.         -RunLevel Highest
  638.  
  639.     # Sending to $null to supress output
  640.     $null = Register-ScheduledTask $taskName -Action $task -Trigger $trigger -Settings $settings -Principal $principal
  641.  
  642.     Write-Host "Registered Scheduled Task: $taskName"
  643. }
  644.  
  645. function Get-WsusIISConfig {
  646.     <#
  647.     .SYNOPSIS
  648.     Returns a hash of all WSUS optimization related IIS settings.
  649.  
  650.     .DESCRIPTION
  651.     Determines WSUS IIS Site and Pool, and then forms hash of all relevant optimization settings.
  652.  
  653.     .LINK
  654.     https://docs.microsoft.com/en-us/powershell/module/webadminstration/get-webapplication?view=winserver2012-ps
  655.  
  656.     .LINK
  657.     https://docs.microsoft.com/en-us/powershell/module/iisadministration/get-iissite?view=win10-ps
  658.     #>
  659.  
  660.     # Get WSUS IIS Index from registry
  661.     $iisSiteIndex = Get-ItemPropertyValue "HKLM:\Software\Microsoft\Update Services\Server\Setup" -Name "IISTargetWebSiteIndex"
  662.  
  663.     # IIS Site
  664.     $iisSiteName = Get-IISSite | Where-Object -Property "Id" -Eq $iisSiteIndex | Select-Object -ExpandProperty "Name"
  665.  
  666.     # Site Application Pool
  667.     $iisAppPool = Get-WebApplication -site $iisSiteName -Name "ClientWebService" | Select-Object -ExpandProperty "applicationPool"
  668.  
  669.     # Application Pool Config
  670.     $iisApplicationPoolConfig = Get-IISConfigCollection -ConfigElement (Get-IISConfigSection -SectionPath "system.applicationHost/applicationPools")
  671.  
  672.     # WSUS Pool Config Root
  673.     $wsusPoolConfig = Get-IISConfigCollectionElement -ConfigCollection $iisApplicationPoolConfig -ConfigAttribute @{"name" = "$iisAppPool" }
  674.  
  675.     # Queue Length
  676.     $queueLength = Get-IISConfigAttributeValue -ConfigElement $wsusPoolConfig -AttributeName "queueLength"
  677.  
  678.     #Load Balancer Capabilities
  679.     $wsusPoolFailureConfig = Get-IISConfigElement -ConfigElement $wsusPoolConfig -ChildElementName "failure"
  680.     $loadBalancerCapabilities = Get-IISConfigAttributeValue -ConfigElement $wsusPoolFailureConfig -AttributeName "loadBalancerCapabilities"
  681.  
  682.     # CPU Reset Interval
  683.     $wsusPoolCpuConfig = Get-IISConfigElement -ConfigElement $wsusPoolConfig -ChildElementName "cpu"
  684.     $cpuResetInterval = (Get-IISConfigAttributeValue -ConfigElement $wsusPoolCpuConfig -AttributeName "resetInterval").TotalMinutes
  685.  
  686.     # Recycling Config Root
  687.     $wsusPoolRecyclingConfig = Get-IISConfigElement -ConfigElement $wsusPoolConfig -ChildElementName "recycling" | Get-IISConfigElement -ChildElementName "periodicRestart"
  688.  
  689.     $recyclingMemory = Get-IISConfigAttributeValue -ConfigElement $wsusPoolRecyclingConfig -AttributeName "memory"
  690.     $recyclingPrivateMemory = Get-IISConfigAttributeValue -ConfigElement $wsusPoolRecyclingConfig -AttributeName "privateMemory"
  691.  
  692.     $clientWebServiceConfig = Get-WebConfiguration -PSPath $iisPath -Filter "system.web/httpRuntime"
  693.  
  694.     $clientMaxRequestLength = $clientWebServiceConfig | select-object -ExpandProperty maxRequestLength
  695.     $clientExecutionTimeout = ($clientWebServiceConfig | select-object -ExpandProperty executionTimeout).TotalSeconds
  696.  
  697.     # Return hash of IIS settings
  698.     @{
  699.         QueueLength              = $queueLength
  700.         LoadBalancerCapabilities = $loadBalancerCapabilities
  701.         CpuResetInterval         = $cpuResetInterval
  702.         RecyclingMemory          = $recyclingMemory
  703.         RecyclingPrivateMemory   = $recyclingPrivateMemory
  704.         ClientMaxRequestLength   = $clientMaxRequestLength
  705.         ClientExecutionTimeout   = $clientExecutionTimeout
  706.     }
  707. }
  708.  
  709. function Get-WsusIISLocalizedNamespacePath {
  710.     # Get localized WSUS IIS web site path: https://docs.microsoft.com/fr-fr/security-updates/windowsupdateservices/18127277 - Document is in English but posted in the French docs
  711.     $iisSitePhysicalPath = Get-ItemPropertyValue -Path 'HKLM:\SOFTWARE\Microsoft\Update Services\Server\Setup\' -Name "TargetDir"
  712.     $iisLocalizedString = Get-Website | Where-Object { $($_.PhysicalPath).StartsWith($iisSitePhysicalPath) } | Select-Object -ExpandProperty Name
  713.     $iisLocalizedNamespacePath = "IIS:\Sites\$iisLocalizedString\ClientWebService"
  714.     return $iisLocalizedNamespacePath
  715. }
  716.  
  717. function Test-WsusIISConfig ($settings, $recommended) {
  718.     <#
  719.     .SYNOPSIS
  720.     Compares current WSUS IIS settings to recommended values.
  721.  
  722.     .DESCRIPTION
  723.     Compares current WSUS IIS settings to recommended values. Prompts user to commit changes.
  724.  
  725.     .PARAMETER settings
  726.     Hash of current WSUS IIS settings.
  727.  
  728.     .PARAMETER recommended
  729.     Hash of recommended WSUS IIS settings.
  730.     #>
  731.  
  732.     # Delay IIS configuration commits until we're done updating all necessary settings
  733.     Start-IISCommitDelay
  734.  
  735.     foreach ($key in $recommended.Keys) {
  736.         # If the current configuration setting doesn't match the recommended value, prompt the user to update
  737.         # This could be better designed to match minimum requirements instead of specific values, but it isn't.
  738.         If ($recommended[$key] -ne $settings[$key]) {
  739.             Write-Host "$key`n`tCurrent:`t$($settings[$key])`n`tRecommended:`t$($recommended[$key])" -BackgroundColor Black -ForegroundColor Red
  740.  
  741.             if (Confirm-Prompt "Update $key to recommended value?") {
  742.                 Update-WsusIISConfig $key $recommended[$key]
  743.             }
  744.         }
  745.         else {
  746.             Write-Host "$key`n`tCurrent:`t$($settings[$key])`n`tRecommended:`t$($recommended[$key])" -BackgroundColor Black -ForegroundColor Green
  747.         }
  748.     }
  749.  
  750.     # Allow IIS config commits again
  751.     Stop-IISCommitDelay
  752. }
  753.  
  754. function Update-WsusIISConfig ($settingKey, $recommendedValue) {
  755.     <#
  756.     .SYNOPSIS
  757.     Modifies IIS configuration for specified setting.
  758.  
  759.     .DESCRIPTION
  760.     Modifies specified IIS setting for WSUS IIS Site/App Pool optimization.
  761.  
  762.     .PARAMETER settingKey
  763.     String used to reference specific IIS configuration setting.
  764.  
  765.     .PARAMETER recommendedValue
  766.     Recommended value for WSUS IIS configuration setting.
  767.     #>
  768.  
  769.     # WSUS IIS Index
  770.     $iisSiteIndex = Get-ItemPropertyValue "HKLM:\Software\Microsoft\Update Services\Server\Setup" -Name "IISTargetWebSiteIndex"
  771.  
  772.     # IIS Site
  773.     $iisSiteName = Get-IISSite | Where-Object -Property "Id" -Eq $iisSiteIndex | Select-Object -ExpandProperty "Name"
  774.  
  775.     # Site Application Pool
  776.     $iisAppPool = Get-WebApplication -site $iisSiteName -Name "ClientWebService" | Select-Object -ExpandProperty "applicationPool"
  777.  
  778.     # Application Pool Config
  779.     $iisApplicationPoolConfig = Get-IISConfigCollection -ConfigElement (Get-IISConfigSection -SectionPath "system.applicationHost/applicationPools")
  780.  
  781.     # WSUS Pool Config Root
  782.     $wsusPoolConfig = Get-IISConfigCollectionElement -ConfigCollection $iisApplicationPoolConfig -ConfigAttribute @{"name" = "$iisAppPool" }
  783.  
  784.     # Recycling Config Root
  785.     $wsusPoolRecyclingConfig = Get-IISConfigElement -ConfigElement $wsusPoolConfig -ChildElementName "recycling" | Get-IISConfigElement -ChildElementName "periodicRestart"
  786.  
  787.     switch ($settingKey) {
  788.         'QueueLength' {
  789.             # Queue Length
  790.             Set-IISConfigAttributeValue -ConfigElement $wsusPoolConfig -AttributeName "queueLength" -AttributeValue $recommendedValue
  791.             Break
  792.         }
  793.         'LoadBalancerCapabilities' {
  794.             # Failure Config Root
  795.             $wsusPoolFailureConfig = Get-IISConfigElement -ConfigElement $wsusPoolConfig -ChildElementName "failure"
  796.  
  797.             # Load Balancer Capabilities
  798.             Set-IISConfigAttributeValue -ConfigElement $wsusPoolFailureConfig -AttributeName "loadBalancerCapabilities" -AttributeValue $recommendedValue
  799.             Break
  800.         }
  801.         'CpuResetInterval' {
  802.             # CPU Reset Interval
  803.             $wsusPoolCpuConfig = Get-IISConfigElement -ConfigElement $wsusPoolConfig -ChildElementName "cpu"
  804.             Set-IISConfigAttributeValue -ConfigElement $wsusPoolCpuConfig -AttributeName "resetInterval" -AttributeValue ([timespan]::FromMinutes($recommendedValue))
  805.             Break
  806.         }
  807.         'RecyclingMemory' {
  808.             Set-IISConfigAttributeValue -ConfigElement $wsusPoolRecyclingConfig -AttributeName "memory" -AttributeValue $recommendedValue
  809.             Break
  810.         }
  811.         'RecyclingPrivateMemory' {
  812.             Set-IISConfigAttributeValue -ConfigElement $wsusPoolRecyclingConfig -AttributeName "privateMemory" -AttributeValue $recommendedValue
  813.             Break
  814.         }
  815.         'ClientMaxRequestLength' {
  816.             # Check if the IIS WSUS Client Web Service web.config is read only and make it RW if so
  817.             Unblock-WebConfigAcl
  818.             Set-WebConfigurationProperty -PSPath $iisPath -Filter "system.web/httpRuntime" -Name "maxRequestLength" -Value $recommendedValue
  819.             Break
  820.         }
  821.         'ClientExecutionTimeout' {
  822.             # Check if the IIS WSUS Client Web Service web.config is read only and make it RW if so
  823.             Unblock-WebConfigAcl
  824.             Set-WebConfigurationProperty -PSPath $iisPath -Filter "system.web/httpRuntime" -Name "executionTimeout" -Value ([timespan]::FromSeconds($recommendedValue))
  825.             Break
  826.         }
  827.         Default {}
  828.     }
  829.  
  830.     Write-Host "Updated IIS Setting: $settingKey, $recommendedValue" -BackgroundColor Green -ForegroundColor Black
  831. }
  832.  
  833. function Remove-Updates ($searchStrings, $updateProp, $force = $false) {
  834.     [reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") | Out-Null
  835.     $wsusServer = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer();
  836.     $scope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
  837.     $updates = $wsusServer.GetUpdates($scope)
  838.     $declinedCount = 0
  839.     $searchCount = 0
  840.     $userMsg = 'Found'
  841.     $color = 'Yellow'
  842.  
  843.     if ($force) {
  844.         $userMsg = 'Declined'
  845.         $color = 'DarkGreen'
  846.     }
  847.  
  848.     Write-Host "Update Property: $updateProp"
  849.  
  850.     foreach ($searchString in $searchStrings) {
  851.         $confirm = $false
  852.         Write-Host " - Update Search: $searchString"
  853.         $searchCount = 0
  854.         foreach ($update in $updates) {
  855.             if ($update.$($updateProp) -match "$searchString") {
  856.                 if ($update.IsDeclined -eq $false) {
  857.  
  858.                     if ($force) {
  859.                         $update.Decline()
  860.                     }
  861.                     $searchCount = $searchCount + 1
  862.                     Write-Host "   [*]$($userMsg): $($update.Title), $($update.ProductTitles) ($searchString)" -ForegroundColor $color
  863.                 }
  864.             }
  865.         }
  866.  
  867.         if ($searchCount -gt 0) {
  868.             Write-Host "$searchCount `"$searchString`" Updates $userMsg!" -ForegroundColor "Blue" -BackgroundColor White
  869.         }
  870.         else {
  871.             Write-Host "      $searchCount `"$searchString`" Updates $userMsg" -ForegroundColor "White"
  872.         }
  873.  
  874.         #Prompt user to confirm declining updates. Do no prompt if force flag is enable to prevent loop
  875.         if ((-not $force) -and ($searchCount -ne 0)) {
  876.             $confirm = Confirm-Prompt "Are you sure you want to decline all ($searchCount) listed ($searchString) updates?"
  877.  
  878.             if ($confirm) {
  879.                 Remove-Updates @($searchString) $updateProp $true | out-null
  880.             }
  881.         }
  882.  
  883.         if (($confirm) -or $force) {
  884.             $declinedCount = ($declinedCount + $searchCount)
  885.         }
  886.     }
  887.  
  888.     return $declinedCount
  889. }
  890.  
  891. function Invoke-DeepClean ($titles, $productTitles) {
  892.     <#
  893.     .SYNOPSIS
  894.     Checks for unneeded WSUS updates to be deleted.
  895.  
  896.     .DESCRIPTION
  897.     Checks for unneeded WSUS updates by product category to be deleted.
  898.  
  899.     .PARAMETER titles
  900.     Array of titles of WSUS titles to search and prompt for removal
  901.  
  902.     .PARAMETER productTitles
  903.     Array of WSUS product titles to search and prompt for removal
  904.  
  905.     .EXAMPLE
  906.     DeepClean $titles $products
  907.  
  908.     .NOTES
  909.     WSUS GetUpdates Method
  910.     https://docs.microsoft.com/en-us/previous-versions/windows/desktop/aa350127(v=vs.85)
  911.  
  912.     WSUS IUpdate Properties
  913.     https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms752741(v=vs.85)
  914.  
  915.     WSUS Product List
  916.     Get-WsusProduct
  917.     https://docs.microsoft.com/en-us/powershell/module/wsus/get-wsusproduct?view=win10-ps
  918.  
  919.     WSUS Classification List
  920.     Get-WsusClassification
  921.     https://docs.microsoft.com/en-us/powershell/module/wsus/get-wsusclassification?view=win10-ps
  922.     #>
  923.  
  924.     $declinedTotal = 0
  925.  
  926.     Write-Host "Make certain to carefully read the listed updates before choosing to remove them!" -BackgroundColor White -ForegroundColor Green
  927.  
  928.     #Remove updates by Title
  929.     Write-Host "Searching for unneeded updates by Title. This process can take a long time. Please wait." -BackgroundColor White -ForegroundColor Blue
  930.     $declinedTotal += Remove-Updates $titles 'Title'
  931.  
  932.     #Remove updates by ProductTitles
  933.     Write-Host "Searching for unneeded updates by ProductTitle. This process can take a long time. Please wait." -BackgroundColor White -ForegroundColor Blue
  934.     $declinedTotal += Remove-Updates $productTitles 'ProductTitles'
  935.  
  936.     #Remove drivers
  937.     Write-Host "Searching for drivers to be removed from WSUS. This process can take a long time. Please wait." -BackgroundColor White -ForegroundColor Blue
  938.     $declinedTotal += Remove-Updates @('Drivers') 'UpdateClassificationTitle'
  939.  
  940.     Write-Host "Searching for unneeded updates superseded by newer updates. This process can take a long time. Please wait." -BackgroundColor White -ForegroundColor Blue
  941.     $declinedTotal += Decline-SupersededUpdates
  942.  
  943.     Write-Host "================DEEPCLEAN COMPLETE==================" -BackgroundColor White -ForegroundColor Blue
  944.     Write-Host "$declinedTotal Total Updates Declined" -BackgroundColor White -ForegroundColor Blue
  945. }
  946.  
  947. function Disable-WsusDriverSync {
  948.     <#
  949.     .SYNOPSIS
  950.     Disable WSUS device driver syncronization and caching.
  951.  
  952.     .DESCRIPTION
  953.     Disable WSUS device driver syncronization and caching. Automatic driver sychronization is one of the primary causes of WSUS slowness, crashing, and wasted storage space.
  954.  
  955.     .LINK
  956.     https://docs.microsoft.com/en-us/powershell/module/updateservices/set-wsusclassification?view=win10-ps
  957.     #>
  958.  
  959.     Get-WsusClassification | Where-Object -FilterScript { $_.Classification.Title -Eq "Drivers" } | Set-WsusClassification -Disable
  960.     Get-WsusClassification | Where-Object -FilterScript { $_.Classification.Title -Eq "Driver Sets" } | Set-WsusClassification -Disable
  961. }
  962.  
  963.  
  964. function Unblock-WebConfigAcl {
  965.     <#
  966.     .SYNOPSIS
  967.     Grants local admins access to web.config
  968.  
  969.     .DESCRIPTION
  970.     Grants BUILTIN\Administrators ownership and read write access to ClientWebService web.config. Also removes Read Only flag.
  971.  
  972.     .LINK
  973.     https://devblogs.microsoft.com/scripting/use-powershell-to-translate-a-users-sid-to-an-active-directory-account-name/
  974.     https://docs.microsoft.com/en-us/dotnet/api/system.security.principal.securityidentifier.-ctor?view=windowsdesktop-5.0#System_Security_Principal_SecurityIdentifier__ctor_System_String_
  975.     #>
  976.  
  977.     $wsusWebConfigPath = Get-WebConfigFile -PSPath $iisPath | Select-Object -ExpandProperty 'FullName'
  978.  
  979.     # Get localized BUILTIN\Administrators group
  980.     $builtinAdminGroup = ([System.Security.Principal.SecurityIdentifier]'S-1-5-32-544').Translate([System.Security.Principal.NTAccount]).Value
  981.  
  982.     Set-FileAclOwner $wsusWebConfigPath $builtinAdminGroup
  983.     Set-FileAclPermissions $wsusWebConfigPath $builtinAdminGroup 'FullControl' 'None' 'None' 'Allow'
  984.     Set-ItemProperty -Path $wsusWebConfigPath -Name IsReadOnly -Value $false
  985. }
  986.  
  987. function Set-FileAclOwner ($file, $owner) {
  988.     <#
  989.     .SYNOPSIS
  990.     Sets NTFS file owner
  991.  
  992.     .DESCRIPTION
  993.     Sets NTFS file owner
  994.  
  995.     .PARAMETER file
  996.     File path as string
  997.  
  998.     .PARAMETER owner
  999.     Account as string to set as owner
  1000.  
  1001.     .LINK
  1002.     https://stackoverflow.com/questions/22988384/powershell-change-owner-of-files-and-folders
  1003.     #>
  1004.  
  1005.     $acl = Get-Acl($file)
  1006.     $account = New-Object -TypeName System.Security.Principal.NTAccount -ArgumentList $owner
  1007.     $acl.SetOwner($account)
  1008.     Set-Acl -Path $file -AclObject $acl
  1009. }
  1010.  
  1011. function Set-FileAclPermissions ($file, $accString, $rights, $inheritanceFlags, $propagationFlags, $type) {
  1012.     <#
  1013.     .SYNOPSIS
  1014.     Set NTFS file permissions
  1015.  
  1016.     .DESCRIPTION
  1017.     Set NTFS permissions for specified file
  1018.  
  1019.     .PARAMETER file
  1020.     File path as string
  1021.  
  1022.     .PARAMETER accString
  1023.     Account to set permissions for as string
  1024.  
  1025.     .PARAMETER rights
  1026.     Access Rights - https://docs.microsoft.com/en-us/dotnet/api/system.security.accesscontrol.filesystemrights?view=dotnet-plat-ext-3.1
  1027.  
  1028.     .PARAMETER inheritanceFlags
  1029.     Inheritence flags - https://docs.microsoft.com/en-us/dotnet/api/system.security.accesscontrol.inheritanceflags?view=dotnet-plat-ext-3.1
  1030.  
  1031.     .PARAMETER propagationFlags
  1032.     Propagation flags - https://docs.microsoft.com/en-us/dotnet/api/system.security.accesscontrol.propagationflags?view=dotnet-plat-ext-3.1
  1033.  
  1034.     .PARAMETER type
  1035.     Access control type - https://docs.microsoft.com/en-us/dotnet/api/system.security.accesscontrol.accesscontroltype?view=dotnet-plat-ext-3.1
  1036.  
  1037.     .LINK
  1038.     https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.security/set-acl?view=powershell-7
  1039.  
  1040.     .LINK
  1041.     https://docs.microsoft.com/en-us/dotnet/api/system.security.accesscontrol.filesystemaccessrule.-ctor?view=dotnet-plat-ext-3.1#System_Security_AccessControl_FileSystemAccessRule__ctor_System_String_System_Security_AccessControl_FileSystemRights_System_Security_AccessControl_InheritanceFlags_System_Security_AccessControl_PropagationFlags_System_Security_AccessControl_AccessControlType_
  1042.     #>
  1043.  
  1044.     $acl = Get-Acl($file)
  1045.     $accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule $accString, $rights, $inheritanceFlags, $propagationFlags, $type
  1046.     $acl.SetAccessRule($accessRule)
  1047.     Set-Acl -Path $file -AclObject $acl
  1048. }
  1049.  
  1050. function Decline-SupersededUpdates ($verbose) {
  1051.     <#
  1052.     .SYNOPSIS
  1053.     Declines approved updates that have been approved and are superseded by other updates.
  1054.  
  1055.     .DESCRIPTION
  1056.     Declines all updates that have been approved and are superseded by other updates. The update will only be declined if a superseding update has been approved.
  1057.  
  1058.     .LINK
  1059.     ApprovedStates - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/aa354257(v=vs.85)
  1060.  
  1061.     .LINK
  1062.     IUpdate - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/bb313429(v=vs.85)
  1063.  
  1064.     .LINK
  1065.     UpdateCollection - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms752803(v=vs.85)
  1066.     #>
  1067.     $declineCount = 0
  1068.     [reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") | Out-Null
  1069.     $wsusServer = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer();
  1070.     $scope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
  1071.  
  1072.     $scope.ApprovedStates = "LatestRevisionApproved"
  1073.     $updates = $wsusServer.GetUpdates($scope)
  1074.  
  1075.     foreach ($update in $updates) {
  1076.         $updatesThatSupersede = $update.GetRelatedUpdates("UpdatesThatSupersedeThisUpdate")
  1077.         if ($updatesThatSupersede.Count -gt 0) {
  1078.             foreach ($super in $updatesThatSupersede) {
  1079.                 if ($super.IsApproved) {
  1080.                     $update.Decline()
  1081.                     $declineCount++
  1082.                     break
  1083.                 }
  1084.             }
  1085.         }
  1086.     }
  1087.  
  1088.     if ($verbose) {
  1089.         Write-Host "Osbolete Updates Declined: $declineCount"
  1090.     }
  1091.     else {
  1092.         return $declineCount
  1093.     }
  1094. }
  1095. #-----------------------------------------------------------[Execution]------------------------------------------------------------
  1096. $iisPath = Get-WsusIISLocalizedNamespacePath
  1097.  
  1098. # Check commandline parameters.
  1099. switch ($true) {
  1100.     ($FirstRun) {
  1101.         Write-Host "All of the following processes are highly recommended!" -ForegroundColor Blue -BackgroundColor White
  1102.  
  1103.         switch ($true) {
  1104.             (Confirm-Prompt "Run WSUS IIS configuration optimization?") {
  1105.                 $wsusIISConfig = Get-WsusIISConfig
  1106.                 Test-WsusIISConfig $wsusIISConfig $recommendedIISSettings
  1107.             }
  1108.             (Confirm-Prompt "Run WSUS database optimization?") {
  1109.                 Optimize-WsusDatabase
  1110.             }
  1111.             (Confirm-Prompt "Run WSUS server optimization?") {
  1112.                 Optimize-WsusUpdates
  1113.             }
  1114.             (Confirm-Prompt "Create daily WSUS server optimization scheduled task?") {
  1115.                 New-WsusMaintainenceTask('Daily')
  1116.             }
  1117.             (Confirm-Prompt "Create weekly WSUS database optimization scheduled task?") {
  1118.                 New-WsusMaintainenceTask('Weekly')
  1119.             }
  1120.             (Confirm-Prompt "Disable device driver synchronization?") {
  1121.                 Disable-WsusDriverSync
  1122.             }
  1123.         }
  1124.         Break
  1125.     }
  1126.     ($DisableDrivers) {
  1127.         Disable-WsusDriverSync
  1128.     }
  1129.     ($DeclineSupersededUpdates) {
  1130.         Decline-SupersededUpdates
  1131.     }
  1132.     ($DeepClean) {
  1133.         Invoke-DeepClean $unneededUpdatesbyTitle $unneededUpdatesbyProductTitles
  1134.     }
  1135.     ($InstallDailyTask) {
  1136.         New-WsusMaintainenceTask('Daily')
  1137.     }
  1138.     ($InstallWeeklyTask) {
  1139.         New-WsusMaintainenceTask('Weekly')
  1140.     }
  1141.     ($CheckConfig) {
  1142.         $wsusIISConfig = Get-WsusIISConfig
  1143.         Test-WsusIISConfig $wsusIISConfig $recommendedIISSettings
  1144.     }
  1145.     ($OptimizeServer) {
  1146.         Optimize-WsusUpdates
  1147.     }
  1148.     ($OptimizeDatabase) {
  1149.         Optimize-WsusDatabase
  1150.     }
  1151. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement