{"id":2551,"date":"2021-04-02T01:55:00","date_gmt":"2021-04-02T01:55:00","guid":{"rendered":"https:\/\/leinss.com\/blog\/?p=2551"},"modified":"2022-06-23T20:16:37","modified_gmt":"2022-06-23T20:16:37","slug":"microsoft-updates-on-demand-part-deux","status":"publish","type":"post","link":"https:\/\/leinss.com\/blog\/?p=2551","title":{"rendered":"Microsoft Updates on Demand &#8211; Part Deux"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Now that you found your favorite patching program, we need to send out notifications and target specific servers for patching.  I schedule updates as an offset from Patch Tuesday.  Patch Tuesday is always the 2nd Tuesday of the month and is when Microsoft releases it&#8217;s monthly patches.  I currently do a pilot group which is fired the night of Patch Tuesday, and then +4, +11 and +18 days after Patch Tuesday, which is every Saturday after Patch Tuesday.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/adamleinss\/PowerShellPatching\/blob\/main\/Send-UpdateNotification2.ps1\" data-type=\"URL\" target=\"_blank\">Send-UpdateNotification2.ps1<\/a> is a notification script I use with SCCM.  This will send out notifications at 8AM on Friday for a maintenance window that takes place Saturday night from 10PM to 5AM.  Due to the way the months work, for +18 days after Patch Tuesday, I send these out on Thursdays instead of Fridays as the script doesn&#8217;t work correctly if the date rolls into the next month.  You would run this script as a scheduled task once a day at 8AM.  Note that this script needs to run from the SCCM server itself.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you are using SCCM, you can set maintenance windows based on offsets from Patch Tuesday using <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/matbe\/PowerShell\/blob\/master\/ConfigMgr\/New-CMMaintenanceWindow.ps1\" target=\"_blank\">New-CMMaintenanceWindow.ps1<\/a>. This is a Powershell script made by Mattias Benninge.  As an example: I run this Powershell script once a year to setup all of my server maintenance windows.  This starts the maintenance window every Saturday after Patch Tuesday of the month starting at 10PM going to 5AM Sunday.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>New-CMMaintenanceWindow.ps1 -SiteCode ABC -MaintenanceWindowName \"+4 days after Patch Tuesday weekend\" -AddMaintenanceWindowNameMonth -CollectionID \"ABC01192\" -patchTuesday -adddays 4 -StartYear 2021 -StartHour 22 -StartMinute 0 -HourDuration 7 -MinuteDuration 0 -SWtype Updates\n\nNew-CMMaintenanceWindow.ps1 -SiteCode ABC -MaintenanceWindowName \"+11 days after Patch Tuesday weekend\" -AddMaintenanceWindowNameMonth -CollectionID \"ABC01193\" -patchTuesday -adddays 11 -StartYear 2021 -StartHour 22 -StartMinute 0 -HourDuration 7 -MinuteDuration 0 -SWtype Updates\n\nNew-CMMaintenanceWindow.ps1 -SiteCode ABC -MaintenanceWindowName \"+18 days after Patch Tuesday weekend\" -AddMaintenanceWindowNameMonth -CollectionID \"ABC01194\" -patchTuesday -adddays 18 -StartYear 2021 -StartHour 22 -StartMinute 0 -HourDuration 7 -MinuteDuration 0 -SWtype Updates<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/adamleinss\/PowerShellPatching\/blob\/main\/Dump-Computers.ps1\" target=\"_blank\">Dump-Computers.ps1<\/a> is used to dump computers from a SCCM collection into a plain text file.  Note that this script needs to run from the SCCM server itself.  If you use WSUS &amp; WSUS groups, take a look at <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/adamleinss\/PowerShellPatching\/blob\/main\/CreateWSUSGroups.ps1\" target=\"_blank\">CreateWSUSGroups.ps1<\/a>.  This script would need to run from the WSUS server itself.  <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Once we gather the plain text files with the different computers, we can copy them to servers.txt.  <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/adamleinss\/PowerShellPatching\/blob\/main\/patchtuesday.ps1\" target=\"_blank\">patchtuesday.ps1<\/a> is a modification of <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/adamleinss\/PowerShellPatching\/blob\/main\/Send-UpdateNotification2.ps1\" target=\"_blank\">Send-UpdateNotification2.ps1<\/a>.  We use this script to copy our specific group of computers to servers.txt based on the offset from Patch Tuesday.  I run this every day as a scheduled task at 8AM.  On Sundays, I run a scheduled task at 1AM and 3AM that runs a batch file,  but I don&#8217;t want to run this outside of the maintenance windows above, so I use <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/adamleinss\/PowerShellPatching\/blob\/main\/patchem.bat\" target=\"_blank\">patchem.bat<\/a>.   The batch file has a &#8220;counter file&#8221; that increments to 2 when servers.txt and serversplus18.txt are equal. When the counter increments to 2, the batch file will no longer try to patch servers.  When the date rolls over to the next month for pilot patching, the counter file is wiped out and the whole process starts over again for that month.  The one thing to note is when you run the batch file in the scheduled task, be sure to set the current working directory to the proper folder in the scheduled task, otherwise it will not work properly (<em>batch files are an ancient technology and assume the current working directory for all operations, if your current working directory is not set to where the batch file is running from, weird things will happen<\/em>).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Some servers just refuse to restart on their own after patching, so we can use <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/adamleinss\/PowerShellPatching\/blob\/main\/restart_sniffer.ps1\" target=\"_blank\">restart_sniffer.ps1<\/a> to nudge them along.  This searches servers.txt and then does a restart on a server if PendingReboot is set to True.  This script uses <a rel=\"noreferrer noopener\" href=\"https:\/\/thesysadminchannel.com\/remotely-check-pending-reboot-status-powershell\/\" target=\"_blank\">Get-PendingRebootStatus.ps1<\/a> from TheSystemAdminChannel website.  I run this every Sunday at 5AM.  Undoubtedly, you&#8217;ll find servers that did not patch because they ran out of disk space.  The &#8220;famous&#8221; servers for doing this are ones that have the IIS role installed since they like to log every web visit.  Take a look at <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/adamleinss\/PowerShellPatching\/blob\/main\/clean_iis_logs.ps1\" target=\"_blank\">clean_iis_logs.ps1<\/a> for how you can keep 14 days of logs and ditch the rest.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The final step is to run a report checking patch compliance.  All of this hard work of getting the monthly updates via an API has been done for us already.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\"><strong>As an update to this part: these 2 scripts would work for a few months, then stop working and after looking at the code, I discovered I could get what I needed with just 5 lines of code.  You can check that out as <a href=\"https:\/\/github.com\/adamleinss\/PowerShellPatching\/blob\/main\/Get-SecurityUpdateAL.ps1\">Get-SecurityUpdateAL.ps1<\/a> on my GitHub site. &#8211; 6\/23\/22.<\/strong><\/mark><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><s><a rel=\"noreferrer noopener\" href=\"https:\/\/sqljana.wordpress.com\/2017\/08\/31\/powershell-get-security-updates-list-from-microsoft-by-monthproductkbcve-with-api\/\" target=\"_blank\">https:\/\/sqljana.wordpress.com\/2017\/08\/31\/powershell-get-security-updates-list-from-microsoft-by-monthproductkbcve-with-api.<\/a>  <mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">As an update: the script on this site stopped working in July 2021, so I&#8217;m using this file instead:<\/mark> <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/meta-l\/MSSecurityUpdates\/blob\/master\/get_updates.ps1\" target=\"_blank\">https:\/\/github.com\/meta-l\/MSSecurityUpdates\/blob\/master\/get_updates.ps1<\/a><\/s>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can ignore instructions for getting your own API key, as it appears that is not necessary anymore.  Note that you will need to install the <a href=\"https:\/\/www.powershellgallery.com\/packages\/MsrcSecurityUpdates\/1.9.5\" target=\"_blank\" rel=\"noreferrer noopener\">MSRCSecurityUpdates<\/a> Powershell module to use this script.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now you can run <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/adamleinss\/PowerShellPatching\/blob\/main\/PatchReport.ps1\" target=\"_blank\">PatchReport.ps1<\/a> to parse the results of this month&#8217;s KBs.  This will search the patches.txt file looking for CUs and monthly rollups for Windows Server 2012, 2012R2, 2016, 2019, 2022 and then run those results against servers.txt that we generated from the Patch Tuesday Powershell scripts above.  I also created <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/adamleinss\/PowerShellPatching\/blob\/main\/PatchReportSMTP.ps1\" target=\"_blank\">PatchReportSMTP.ps1<\/a> to send e-mails of the same report to me.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can see what CUs are available for this month from <a href=\"https:\/\/portal.msrc.microsoft.com\/en-us\/security-guidance\">https:\/\/portal.msrc.microsoft.com\/en-us\/security-guidance<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A sample patch compliance report is shown below.  If a row in the column in InstalledBy is empty, that means that server did not restart after patching and is not fully patched (<em>yet<\/em>).<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/leinss.com\/images\/patch_report.png?w=525\" alt=\"\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Any missing patches will show up like this in orange text:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/leinss.com\/images\/patch_report2.png?w=525&#038;ssl=1\" alt=\"\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">I commented out the error code about &#8220;Cannot connect to computer X&#8221;, since I run the same script against the same servers.txt file in two different domains. About 9 out of 10 times, a failure to patch is a low disk space issue.  In a previous <a rel=\"noreferrer noopener\" href=\"https:\/\/leinss.com\/blog\/?p=2482\" target=\"_blank\">blog posting<\/a>, I talked about how disabling the HTTP.SYS driver blocked patching and the remedy for that, though something like that happening is pretty rare.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Soli Deo Gloria<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Now that you found your favorite patching program, we need to send out notifications and target specific servers for patching. I schedule updates as an offset from Patch Tuesday. Patch Tuesday is always the 2nd Tuesday of the month and is when Microsoft releases it&#8217;s monthly patches. I currently do a pilot group which is &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/leinss.com\/blog\/?p=2551\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Microsoft Updates on Demand &#8211; Part Deux&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[4,11,12,7],"tags":[],"class_list":["post-2551","post","type-post","status-publish","format-standard","hentry","category-operating-system","category-powershell","category-sccm","category-tech-tips"],"jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p7wiG8-F9","jetpack-related-posts":[],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/leinss.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/2551","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/leinss.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/leinss.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/leinss.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/leinss.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2551"}],"version-history":[{"count":77,"href":"https:\/\/leinss.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/2551\/revisions"}],"predecessor-version":[{"id":3111,"href":"https:\/\/leinss.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/2551\/revisions\/3111"}],"wp:attachment":[{"href":"https:\/\/leinss.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2551"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/leinss.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2551"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/leinss.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2551"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}