Advertisement
Journeym

Untitled

Jun 12th, 2018
1,114
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <#  
  2. .SYNOPSIS  
  3.     DELAY WSUS AUTO APPROVALS
  4.  
  5. .DESCRIPTION  
  6.     This script delay approvals for specific target group (OU).
  7.     The delay is customizable.
  8.     You must run this script on your WSUS Server.
  9. .NOTES  
  10.     File Name            : DELAY-WSUS-AUTO-APPROVALS.ps1  
  11.     Author               : Igors Moskalcovs
  12.     Version              : 1.4 (2018/06/12) - added allowed classification configuration
  13.                                             - made report look pretier and more informative
  14.                                             - added send to email functionality
  15. ----------------------------------------------------------------------------------------
  16.     Version              : 1.3 (2013/03/05) - make optional validation targetgroup
  17.                                             - count delay from Creation Date (if validation target group exists) else from Arrival Date of updates
  18.     History              : 1.2 (2013/02/14) - Fix
  19.     History              : 1.1 (2013/01/29) - Fix
  20.     History              : 1.0 (2013/01/22) - First Version
  21. #>
  22.  
  23.  
  24.  
  25.  
  26.  
  27. ###############################################################################
  28. # GLOBALS
  29.  
  30. ##################
  31. # WSUS Part
  32.  
  33. # This the delay between arrival date of updates and automatic approvals
  34. $DELAY = [TimeSpan]30d ;  
  35.  
  36. # Only these categories and classifications are included
  37. $WSUS_CategoriesIncluded = @("Office 2007", "Office 2016", "Windows 7", "Windows Defender");  
  38. $WSUS_ClassificationsIncluded = @("Critical Updates", "Definition Updates", "Security Updates", "Tools", "Update Rollups", "Updates");
  39.  
  40. # We approve updates on this TargetGroup
  41. $WSUS_TargetGroup_To_Approved = "Windows 7";
  42.  
  43. # Optional : Validation TargetGroup
  44. # If you don't have/want a Validation Target Group (with limited computers), set variable to null.
  45. #$WSUS_TargetGroup_Validation = "Validation";
  46. $WSUS_TargetGroup_Validation = "Updates Test Pool";
  47.  
  48. #Email Notifications
  49. #Server
  50. $mxserver = "mx.contoso.com"
  51. #From
  52. $emailfrom = "wsus <wsus@contoso.com>"
  53. #Recipients
  54. $emailrecipients = "User <user@contoso.com>", "User2 <user2@contoso.com>", "User3 <user3@contoso.com>"
  55. #Subject
  56. $emailsubject = "Wsus Delayed Updates Report"
  57. #Body
  58. $emailbody = ""
  59.  
  60. ##################
  61. # LOG, REPORT and RUN files
  62.  
  63. $PATH_DIR = "D:\DELAY-WSUS-AUTO-APPROVALS";
  64. $FILENAME_LOG = "DELAY-WSUS-AUTO-APPROVALS-W7.log"
  65. $FILENAME_REPORTS = "DELAY-WSUS-AUTO-APPROVALS_" + $(Get-Date -format yyyy-MM-dd_HH\hmm\mss\s) + "W7.html"
  66. $TITLE = "DELAY WSUS AUTO APPROVALS"
  67.  
  68.  
  69.  
  70. ###############################################################################
  71. # FUNCTIONS
  72.  
  73. function BuildHTMLTable($report) {
  74.     # The content
  75.     if ($report.count -eq 0) {
  76.         return "<h3>No updates</h3>"
  77.     }
  78.     else {
  79.         # $WSUS_TargetGroup_Validation is optinal  
  80.         if ( $WSUS_TargetGroup_Validation -ne $null ) {
  81.            
  82.             $rows = "<p></p>`
  83.               <p><h2>Status Report</b></h2><table>`
  84.               <p></p>`
  85.               <col width=150px> `
  86.               <col width=150px> `
  87.               <col width=150px> `
  88.               <tr>`
  89.               <th>Approved</th>`
  90.               <th>Waiting (Delay)</th>`
  91.               <th>Waiting (Validation)</th>`
  92.               </tr> `
  93.               <tr> `
  94.                   <td>" + $(($report | Where-Object -FilterScript {$_.Action -eq "Approve !"} | Measure-Object).Count) + "</td>`
  95.                   <td>" + $(($report | Where-Object -FilterScript {$_.Description -like "*days remaining"} | Measure-Object).Count) + "</td>`
  96.                   <td>" + $(($report | Where-Object -FilterScript {$_.Description -like "*must be approved first"} | Measure-Object).Count) + "</td>`
  97.               </tr> `
  98.               </table> `
  99.               <p></p> `
  100.               <p><h2>Detailed Report</h2></p>`
  101.               <p><i>Results are sorted by arrival date</i></p>`
  102.               <table> `
  103.               <col> `
  104.               <col> `
  105.               <col> `
  106.               <col> `
  107.               <col> `
  108.               <col> `
  109.               <col> `
  110.               <tr>`
  111.               <th>Name</th>`
  112.               <th>Product</th>`
  113.               <th>Classification</th>`
  114.               <th>Date of Arrivals</th>`
  115.               <th>Approved on Validation Group</th>`
  116.               <th>Action</th>`
  117.               <th>Description</th>`
  118.               </tr>"
  119.             $rows += $report | Sort-Object "Date of Arrival" -descending  | `
  120.                 ForEach-Object { "`
  121.                   <tr>`
  122.                   <td>" + $($_."name") + "</td>`
  123.                   <td>" + $($_."Product") + "</td>`
  124.                   <td>" + $($_."Classification") + "</td>`
  125.                   <td>" + $($_."Date of Arrival") + "</td>`
  126.                   <td style='text-align:center'>" + $($_."Approved by Validation") + "</td>`
  127.                   <td style='text-align:center'>" + $($_."Action") + "</td>`
  128.                   <td>" + $($_."Description") + "</td>`
  129.                   </tr>" }
  130.             $rows += "</table>"
  131.         }
  132.         else {
  133.             $rows = "<p></p>`
  134.            <p><h2>Status Report</b></h2><table>`
  135.            <p></p>`
  136.            <col width=150px> `
  137.            <col width=150px> `
  138.            <col width=150px> `
  139.            <tr>`
  140.            <th>Approved</th>`
  141.            <th>Waiting (Delay)</th>`
  142.            <th>Waiting (Validation)</th>`
  143.            </tr> `
  144.            <tr> `
  145.                <td>" + $(($report | Where-Object -FilterScript {$_.Action -like "Approve !"} | Measure-Object).Count) + "</td>`
  146.                <td>" + $(($report | Where-Object -FilterScript {$_.Description -like "*days remaining"} | Measure-Object).Count) + "</td>`
  147.                <td>" + $(($report | Where-Object -FilterScript {$_.Description -like "*must be approved first"} | Measure-Object).Count) + "</td>`
  148.            </tr> `
  149.            </table> `
  150.            <p></p> `
  151.            <p><h2>Detailed Report</h2></p>`
  152.            <p><i>Results are sorted by arrival date</i></p>`
  153.            <table>`
  154.               <col>`
  155.               <col>`
  156.               <col>`
  157.               <col> `
  158.               <col> `
  159.               <col> `
  160.               <tr>`
  161.               <th>Name</th>`
  162.               <th>Product</th>`
  163.               <th>Classification</th>`
  164.               <th>Date of Arrivals</th>`
  165.               <th>Action</th>`
  166.               <th>Description</th>`
  167.               </tr>"
  168.             $rows += $report | Sort-Object "Date of Arrival" -descending  | `
  169.                 ForEach-Object { "`
  170.                   <tr>`
  171.                   <td>" + $($_."name") + "</td>`
  172.                   <td>" + $($_."Product") + "</td>`
  173.                   <td>" + $($_."Classification") + "</td>`
  174.                   <td>" + $($_."Date of Arrival") + "</td>`
  175.                   <td style='text-align:center'>" + $($_."Action") + "</td>`
  176.                   <td>" + $($_."Description") + "</td>`
  177.                   </tr>" }
  178.             $rows += "</table>"
  179.         }
  180.     }
  181.     return $rows
  182. }
  183.  
  184.  
  185. function filterUpdates($updates) {
  186.  
  187.     $updates_filtered = @()
  188.     $nb_rejected = 0
  189.    
  190.     # Get update not approved for no targetgroup
  191.     $updates | foreach {
  192.         if ( $_.IsApproved -eq $False ) {
  193.             $updates_filtered += $_
  194.         }
  195.     }
  196.  
  197.     # Get update approved only for a specific Targetgroup
  198.     $updates | where { $_.IsApproved -eq $True } | foreach { `
  199.         $mark_tg_approved = $false
  200.         $_.getUpdateApprovals() | foreach {
  201.             if ($_.ComputerTargetGroupId -eq $tg_to_Approve.id ) {
  202.                 $mark_tg_approved = $true
  203.             }
  204.         }
  205.         if ( $mark_tg_approved ) {
  206.             $nb_rejected++
  207.         }
  208.         else {
  209.             $updates_filtered += $_
  210.         }
  211.    
  212.     }
  213.  
  214.     return ($updates_filtered, $nb_rejected)
  215. }
  216.  
  217.  
  218. ###############################################################################
  219. # CORE PROGRAM - DON'T TOUCH !!! ... Normally
  220.  
  221.  
  222.  
  223. ####
  224. # INIT
  225. ####
  226. # init trap
  227. $logfile = $PATH_DIR + "\" + $FILENAME_LOG
  228. trap {Add-content $logfile -value "$(Get-Date -format 'yyyy/MM/dd HH:mm:ss') Error - $_"; break}
  229. # get date
  230. $now = Get-Date
  231. # build filename for reports
  232. $report_filename = $PATH_DIR + "\" + $FILENAME_REPORTS
  233. $ErrorActionPreference = "Stop"
  234.  
  235. ####
  236. # Log
  237. ####
  238. Add-content $logfile -value "----------------------------------------------------"
  239. Add-content $logfile -value "$(Get-Date -format 'yyyy/MM/dd HH:mm:ss') Start"
  240.  
  241.  
  242.  
  243. ####
  244. # Load Assembly
  245. ####
  246. Add-content $logfile -value "$(Get-Date -format 'yyyy/MM/dd HH:mm:ss') Loading Assembly ... "
  247. [reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") | Out-Null
  248. $wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer()
  249. # GetID of TargetGroup
  250. $tg_to_Approve = $wsus.GetComputerTargetGroups() | where { $_.name -eq $WSUS_TargetGroup_To_Approved }
  251. # $WSUS_TargetGroup_Validation is optinal  
  252. if ( $WSUS_TargetGroup_Validation -ne $null ) {
  253.     $tg_valid_Approve = $wsus.GetComputerTargetGroups() | where { $_.name -eq $WSUS_TargetGroup_Validation }
  254. }
  255.  
  256.  
  257.  
  258.  
  259. ####
  260. # Get all updates we want.
  261. ####
  262. Add-content $logfile -value "$(Get-Date -format 'yyyy/MM/dd HH:mm:ss') Collect and filter updates ... "
  263.  
  264. # building Rexgexp
  265. $regexpCateg = ""; $WSUS_CategoriesIncluded | foreach { $regexpCateg += "^$_$|" }
  266. $regexpCateg = $regexpCateg.substring(0, $regexpCateg.length - 1)
  267. $regexpClass = ""; $WSUS_ClassificationsIncluded | foreach { $regexpClass += "^$_$|" }
  268. $regexpClass = $regexpClass.substring(0, $regexpClass.length - 1)
  269. $allUpdates = $wsus.getUpdates()
  270. $updates = $allUpdates | Where {$_.ProductTitles -match $regexpCateg `
  271.         -and $_.UpdateClassificationTitle -match $regexpClass `
  272.         -and $_.IsDeclined -eq $False `
  273.         -and $_.IsSuperseded -eq $False `
  274.         -and $_.State -eq "Ready"}
  275.  
  276. Add-content $logfile -value "$(Get-Date -format 'yyyy/MM/dd HH:mm:ss') - Total Updates : $($allUpdates.count)     Useful Updates : $($updates.count)"
  277.  
  278.  
  279.  
  280.  
  281. ####
  282. # Eliminates some updates we collect
  283. ####
  284. Add-content $logfile -value "$(Get-Date -format 'yyyy/MM/dd HH:mm:ss') Reject some useless updates ... "
  285.  
  286. ($updates_filtered, $nb_rejected) = filterUpdates($updates)
  287.  
  288. Add-content $logfile -value "$(Get-Date -format 'yyyy/MM/dd HH:mm:ss') - Total updates rejected $nb_rejected (because they are already approved) "
  289.  
  290.  
  291.  
  292.  
  293.  
  294. ####
  295. # Automatic Approve
  296. # and construct list of each updates
  297. ####
  298. Add-content $logfile -value "$(Get-Date -format 'yyyy/MM/dd HH:mm:ss') Approving updates ... "
  299.  
  300. # Title, arrivaldate, producttitles, is not superseded, approved by validation, delay OK, action, remark
  301. $report = @()
  302. $nb_approved = 0
  303. $updates_filtered | foreach {
  304.     $action = @()
  305.     $desc = @()
  306.    
  307.    
  308.     # $WSUS_TargetGroup_Validation is optinal  
  309.     if ( $WSUS_TargetGroup_Validation -ne $null ) {
  310.         $isValidationApproved = $false
  311.         $isDelayExceeded = $false
  312.         if ( $_.isApproved ) {
  313.             $_.getUpdateApprovals() | foreach {  
  314.                 if ($_.ComputerTargetGroupId -eq $tg_valid_Approve.id ) {  
  315.                     $isValidationApproved = $true
  316.                     $delayCount = $_.CreationDate.add($DELAY)
  317.                     $isDelayExceeded = $delayCount -lt $now
  318.                 }
  319.             }
  320.         }
  321.     }
  322.     else {
  323.         $delayCount = $_.ArrivalDate.add($DELAY)
  324.         $isDelayExceeded = $delayCount -lt $now
  325.         $isValidationApproved = $true
  326.     }
  327.    
  328.  
  329.     # $WSUS_TargetGroup_Validation is optinal  
  330.     if ($isValidationApproved -or $WSUS_TargetGroup_Validation -eq $null ) {
  331.         if ($isDelayExceeded) {
  332.             Add-content $logfile -value "$(Get-Date -format 'yyyy/MM/dd HH:mm:ss') - Approve $($_.legacyname) "
  333.             $nb_approved++
  334.             $action = "(Approve !)"
  335.             $desc = "Uncomment action in script"
  336.             ## UNCOMMENT WHEN YOU'RE SURE  
  337.             $action="Approve !"
  338.             $desc = "$($($now - $_.ArrivalDate).days) days after release"
  339.             if($_.RequiresLicenseAgreementAcceptance) {$_.AcceptLicenseAgreement()}
  340.             $_.approve(“Install”,$tg_to_Approve)
  341.         }
  342.         else {          
  343.             $action = "Wait"
  344.             $desc = "$($($delayCount - $now).days) days remaining"
  345.         }
  346.     }
  347.     else {
  348.         if ( $WSUS_TargetGroup_Validation -ne $null ) {
  349.             $action = "Wait"
  350.             $desc = "$($tg_valid_Approve.name) must be approved first"
  351.         }
  352.         else {
  353.             $action = "Wait"
  354.             $desc = "This case never happen - send comments on script center for this script"
  355.         }
  356.     }
  357.  
  358.  
  359.     $line = New-Object System.Object
  360.     $line | Add-Member -type NoteProperty -name "Name" -value $_.title
  361.     $line | Add-Member -type NoteProperty -name "Date of Arrival" -value $_.ArrivalDate.ToString("yyyy/MM/dd HH:mm:ss")
  362.     $line | Add-Member -type NoteProperty -name "Product" -value $_.ProductTitles
  363.     $line | Add-Member -type NoteProperty -name "Classification" -value $_.UpdateClassificationTitle
  364.     # $WSUS_TargetGroup_Validation is optinal  
  365.     if ( $WSUS_TargetGroup_Validation -ne $null ) {
  366.         $line | Add-Member -type NoteProperty -name "Approved by Validation" -value $isValidationApproved
  367.     }
  368.     $line | Add-Member -type NoteProperty -name "Action" -value $action
  369.     $line | Add-Member -type NoteProperty -name "Description" -value $desc
  370.     $report += $line
  371. }
  372. Add-content $logfile -value "$(Get-Date -format 'yyyy/MM/dd HH:mm:ss') - Total updates approved $nb_approved "
  373.  
  374.  
  375.  
  376. ####
  377. # Build report
  378. ####
  379. Add-content $logfile -value "$(Get-Date -format 'yyyy/MM/dd HH:mm:ss') Building report ... "
  380.  
  381.  
  382. $HTML_head = "<!--mce:0-->";
  383. $HTML_style = "
  384. <style>`
  385. i, table, tr > th {`
  386.    font-family: Tahoma, Geneva, sans-serif;`
  387.    font-size: 10px;`
  388.    text-align:left;`
  389. }`
  390. p {`
  391.    font-family: Tahoma, Geneva, sans-serif;`
  392.    font-size: 12px;`
  393. }`
  394. h1,h2,h3 {`
  395.    font-family: Tahoma, Geneva, sans-serif;`
  396. }`
  397. tr > td {`
  398.   text-align:left;`
  399. }`
  400. ul {`
  401.    list-style-type: circle;`
  402.    font-family: Tahoma, Geneva, sans-serif;`
  403.    font-size: 12px;`
  404. }`
  405. </style>`
  406. "
  407.    
  408. $HTML_body = "<H2> $TITLE - Date of report : $($now.ToString("yyyy/MM/dd HH:mm:ss"))</H2>`
  409.   <p>Delay <b>$($DELAY.days) days</b> is set for auto approval of updates on a target group <b>$WSUS_TargetGroup_To_Approved</b></p> `
  410.   <p>Validation group is set to : $WSUS_TargetGroup_Validation group on WSUS Server</p>`
  411.   <p><h3>This script is taking into accout only the following updates:</h3></p>`
  412.   <ul>`
  413.   <li>For products matching $([system.String]::Join(",", $WSUS_CategoriesIncluded))</li>`
  414.   <p><i>   To configure, change WSUS_CategoriesIncluded script variable</i></p>`
  415.   <li>For classification matching $([system.String]::Join(",", $WSUS_ClassificationsIncluded))</li>`
  416.   <p><i>   To configure, change WSUS_ClassificationsIncluded script variable</i></p>`
  417.   <li>That aren't Declined</li>`
  418.   <li>That aren't Superseded</li>`
  419.   <li>That are marked as Needed</li>`
  420.   <p><i>this setting is set by WSUS server, when at least one of the registered workstations reported it as needed</i></p>`
  421.   </ul>"
  422.  
  423. $report_html = BuildHTMLTable($report)
  424.  
  425. $html = "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN'  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>`
  426.   <html xmlns='http://www.w3.org/1999/xhtml'>" + `
  427. "<head>" + `
  428. "<title>" + $TITLE + "</title>" + `
  429. $HTML_style + `
  430. "</head>" + `
  431. "<body>" + `
  432. $HTML_body + `
  433. $report_html + `
  434. "</body>" + `
  435. "</html>"
  436.  
  437.  
  438. $html | Set-Content $report_filename
  439. Send-MailMessage -From $emailfrom -To $emailrecipients -Subject $emailsubject -SmtpServer $mxserver -Attachments $report_filename
  440.  
  441. ####
  442. # End
  443. ####
  444.  
  445. Add-content $logfile -value "$(Get-Date -format 'yyyy/MM/dd HH:mm:ss') End "
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement