I don't have a blog...but I can sure share with this group.  I have attached 2 
files, one is the PowerShell script and the other is a text file that the 
script uses for controlling the updates that get automatically deployed.

Some notes about how I have things set up before I go into what all the script 
does:

·         For Software Update Groups/Packages, I have one group per month for 
the current year, and then consolidate older updates from previous years into a 
single group, i.e. "2013-All"

o   Packages are created using this same standard but are prefixed with  
"Software Updates -", i.e "Software Updates - 2013-All" or "Software Updates - 
2014-09"

·         I created an ADR ahead of time that the script utilizes, these 
settings will need to be configured:

o   General Tab

§  Collection - Doesn't matter, I set mine to an empty collection b/c I delete 
the group/deployment that is created when the ADR runs

§  Add to an existing Software Update Group

o   Deployment Settings Tab

§  Automatically deploy all software updates found by this rule and approve any 
license agreements

o   Evaluation Schedule

§  Do not run this rule automatically

o   Software Updates Tab

§  Can be set to anything, script will overwrite

o   Deployment Package Tab

§  Set to any existing package, script will overwrite


Now, this is what the script does...

·         Loops through all the existing SU groups that start with "20", so it 
gets all the standard monthly groups, and then looks for any required updates 
released during the relevant month/year

o   If SU Group doesn't exist for current month, it will then look for updates 
for the current month

o   I also have some other non-standard groups for SQL SPs and such that I 
didn't want included in the normal monthly updates, so they are ignored by this 
script

·         For each group, it will then search for updates that meet this 
criteria using WMI (SMS_SoftwareUpdate class):

o   IsDeployed=0 AND IsSuperseded=0 AND IsExpired=0 AND NumMissing > 0

o   DatePosted matches the Year and/or Month of the Group being processed

·         When updates are found that need to be deployed:

o   Verify that required package exists, if not it will create a new package

o   Modify ADR to use the appropriate package

o   Modify ADR to use the appropriate Search Criteria

§  Required > 1

§  Superseded = No

§  Title - it will add each Update the script found to the Title search 
crieteria

o    Run the ADR (using ConfigMgr cmdlet 
"Invoke-CMSoftwareUpdateAutoDeploymentRule")

o   Waits for the ADR to complete and will logs/write-host the number of 
updates that have been downloaded

o   Deletes the Software Update Group created by the ADR

o   Verifies all the updates are downloaded, if an update wasn't downloaded, it 
will modify/run the ADR again

o   Validates the SU Package is on all DPs (does this based on DP Group)

o   Adds updates to existing SU Group or creates a new SU Group if a new month

§  If it creates a new group, it will also create the required deployments


Customizations for your environment:
Lines 110-114 - These lines create the deployments for a newly create group, we 
create 2 deployments...you can modify as needed to fit your deployment strategy
Lines 125 & 128 - Modify Package Source path as needed
Line 163 - there are a couple of strings that would need to be modified, needs 
the site server and site code for your environment
Line 178 - Name of the DP group that includes all of the distribution points
Line 180 - Name of the ADR you want to use


The script also includes a -Review parameter that allows you just see the 
updates that would be deployed.  It will also do some simple logging, creates a 
log file in the same dir the script is run from.  Feel free to use any/all of 
the script, I have been using it for 7-8 months and it has worked great for me, 
but as always, I recommend testing in your environment!!



From: [email protected] [mailto:[email protected]] On 
Behalf Of s kissel
Sent: Tuesday, October 28, 2014 11:50 AM
To: [email protected]
Subject: RE: [mssms] ADR and Dynamic Deployment Packages

Scott,

That sounds like what we're looking for. Care to strip any proprietary info out 
of the script (Host names, etc.) and share? Have you blogged it? If not, you 
should! Sounds like there are at least two other folks in this thread who would 
be interested in something of this sort and have so far come up without a 
solution.

Regards,
-S
________________________________
From: [email protected]<mailto:[email protected]>
To: [email protected]<mailto:[email protected]>
Subject: RE: [mssms] ADR and Dynamic Deployment Packages
Date: Tue, 28 Oct 2014 16:33:42 +0000
We have separate SU Group and packages for each month and couldn't get the ADR 
process to handle this out of the box.  I wrote a PowerShell script to handle 
all of it.  It will create a new package and modify the ADR to use the new 
package, and then invoke the ADR.  I also wanted more control over which 
updates get flagged when the ADR runs, so the script will also modify the 
search criteria each time it runs.

From: [email protected]<mailto:[email protected]> 
[mailto:[email protected]] On Behalf Of s kissel
Sent: Monday, October 27, 2014 3:27 PM
To: [email protected]<mailto:[email protected]>
Subject: [mssms] ADR and Dynamic Deployment Packages

Hi - Suppose you had an ADR that created a new SU Group every month. You can 
set the ADR up to download the updates into a new SU package, or a previously 
created one. However, suppose you wanted the ADR to create a new SU package 
each month, using variables such as %Month% or something along those lines. Is 
this even possible?

The main driver for this is that with 160 DPs spread worldwide and some with 
very, very low bandwidth available, it's become increasingly difficult to push 
6 and 9 GB packages across WAN links repeatedly, and even more so as a few new 
DPs are stood up every month.

Regards,
-S

**************************************************************************************************
Note:
The information contained in this message may be privileged and confidential and
protected from disclosure.  If the reader of this message is not the intended
recipient, or an employee or agent responsible for delivering this message to
the intended recipient, you are hereby notified that any dissemination,
distribution or copying of this communication is strictly prohibited. If you
have received this communication in error, please notify us immediately by
replying to the message and deleting it from your computer.
**************************************************************************************************

**************************************************************************************************
Note: 
The information contained in this message may be privileged and confidential 
and 
protected from disclosure.  If the reader of this message is not the intended  
recipient, or an employee or agent responsible for delivering this message to  
the intended recipient, you are hereby notified that any dissemination,   
distribution or copying of this communication is strictly prohibited. If you  
have received this communication in error, please notify us immediately by  
replying to the message and deleting it from your computer. 
**************************************************************************************************



Param (
        [Parameter(ParameterSetName='Review')]
        [switch] $Review
)

Function LogWrite($Message, $AddDate)
{
        If ($AddDate) {$Message = "$(Get-Date) - $Message"}
        write-host $Message; write-output $Message | Out-File -FilePath 
$LogName -Append -NoClobber -Encoding default
}

Function ProcessSoftwareUpdateGroup($SoftwareUpdateGroupName,$ExistingGroup)
{
        LogWrite("Processing Update Group: $SoftwareUpdateGroupName")($true)
        
        #Search for updates
        $UpdateIDs = @()
        $UpdateTitles = @()
        $SoftwareUpdateGroupNameArray = $SoftwareUpdateGroupName.Split("-")
        $UpdateGroupYear = $SoftwareUpdateGroupNameArray[0]
        $UpdateGroupMonth = $SoftwareUpdateGroupNameArray[1]
        If ($UpdateGroupMonth.substring(0,1) -eq "0") {$UpdateGroupMonth = 
$UpdateGroupMonth.substring(1,1)}
        
        LogWrite("Searching for updates...")($true)
        Foreach ($SoftwareUpdate in (Get-WmiObject -ComputerName $CMServer 
-NameSpace "root\SMS\Site_$SiteCode" -Class "SMS_SoftwareUpdate" -Filter 
"IsDeployed=0 AND IsSuperseded=0 AND IsExpired=0 AND NumMissing > 0") | 
Sort-Object LocalizedDisplayName)
        {
                $swbem.Value = $SoftwareUpdate.DatePosted; $UpdatePosted = 
$swbem.GetVarDate()

                If ($UpdateGroupYear -eq $UpdatePosted.year -and 
(($UpdateGroupMonth -eq "All") -or ($UpdateGroupMonth -eq $UpdatePosted.month)))
                {
                        Foreach ($UpdateString in $UpdateNameStrings) {If 
($($SoftwareUpdate.LocalizedDisplayName).Contains($UpdateString)) {LogWrite 
($SoftwareUpdate.LocalizedDisplayName)($false); $UpdateIDs += 
$SoftwareUpdate.CI_ID; $UpdateTitles += $SoftwareUpdate.LocalizedDisplayName}}
                }
        }
        
        LogWrite("Updates to add: $($UpdateIDs.Count)")($true)
        
        If ($PsCmdlet.ParameterSetName -eq "Review") {write-host "Reviewing 
updates, will not deploy"; return}

        #Verify Package Exists and is on all DPs if it is an existing group or 
ther are updates to deploy
        If ($ExistingGroup -or ($UpdateIDs.Count -gt 0)) {$SUPackage = 
CheckPackageExists($SoftwareUpdateGroupName)}
        
        If ($UpdateIDs.Count -gt 0)
        {
                #Modify Automatic Deployment Rule to use the appropriate Package
                [WMI]$AutoDeployment = (Get-WmiObject -ComputerName $CMServer 
-Namespace "root\sms\Site_$SiteCode" -Class "SMS_AutoDeployment" | Where-Object 
-FilterScript {$_.Name -eq $ADRName}).__PATH
                [XML]$ContentTemplateXML = $AutoDeployment.ContentTemplate

                $ContentTemplateXML.ContentActionXML.PackageId = 
$SUPackage.PackageID
                $AutoDeployment.ContentTemplate = $ContentTemplateXML.OuterXML
                $AutoDeployment.Put() | Out-Null
                LogWrite ("Modified Automatic Deployment rule to use 
appropriate package: $($SUPackage.PackageID)")($true)
                
                #Modify Automatic Deployment Rule with appropriate search 
criteria
                Set-CMSoftwareUpdateAutoDeploymentRule -Name $ADRName -Required 
">=1" -Superseded $false -Title $UpdateTitles -Force
                LogWrite ("Modified Automatic Deployment rule to use 
appropriate search criteria")($true)
                
                $ADRStartTime = Get-Date
                
                #Run the Automatic Deployment Rule
                Invoke-CMSoftwareUpdateAutoDeploymentRule -Name $ADRName
                LogWrite("Invoked Automatic Deployment Rule: $ADRName")($true)
                
                #Wait for Automatic Deployment to run
                LogWrite ("Checking status of Automatic Deployment Rule")($true)
                Do
                {
                        $ADRLastRunTime = 
(Get-CMSoftwareUpdateAutoDeploymentRule -Name $ADRName).LastRunTime
                        If ($ADRLastRunTime -gt $ADRStartTime) {$DownloadDone = 
$true; LogWrite("Automatic Deployment Rule has completed")($true)}
                        Else 
                        {
                                $DownloadedUpdates = 0
                                Foreach ($UpdateID in $UpdateIDs)
                                {
                                        If ((Get-WmiObject -ComputerName 
$CMServer -NameSpace "root\SMS\Site_$SiteCode" -Class "SMS_SoftwareUpdate" 
-Filter "CI_ID='$UpdateID'").IsContentProvisioned) {$DownloadedUpdates++}
                                }
                                LogWrite ("Automatic Deployment Rule is running 
($DownloadedUpdates of $($UpdateIDs.Count))")($true); Start-Sleep -s 60
                        }
                }
                Until ($DownloadDone -eq $true)
                
                #Clean up the ADR group/deployment
                Remove-CMSoftwareUpdateGroup -Name $ADRName -Force
                LogWrite("Deleted Software Update Group: $ADRName")($true)

                #Check to see if Updates are downloaded
                Foreach ($UpdateID in $UpdateIDs)
                {
                        $SoftwareUpdate = Get-WmiObject -ComputerName $CMServer 
-NameSpace "root\SMS\Site_$SiteCode" -Class "SMS_SoftwareUpdate" -Filter 
"CI_ID='$UpdateID'"
                        If (!($SoftwareUpdate.IsContentProvisioned)) 
{DownloadIndividualUpdate($SoftwareUpdate.LocalizedDisplayName)}
                }
                
                #Verify Package is on all DPs
                If (Get-WmiObject -ComputerName $CMServer -NameSpace 
"root\SMS\Site_$SiteCode" -Class "SMS_DPGroupPackages" -Filter 
"PkgID='$($SUPackage.PackageID)' AND GroupID='$AllDPGroupID'") {}
                Else {Start-CMContentDistribution -DeploymentPackageId 
"$($SUPackage.PackageID)" -DistributionPointGroupName "All Distribution 
Points"; LogWrite ("Content sent to All Distribution Points")($true)}
                
                #Add to or create Software Update group
                If ($ExistingGroup)
                {
                        #Add Updates to Software Update Group
                        Foreach ($UpdateID in $UpdateIDs) 
{Add-CMSoftwareUpdateToGroup -SoftwareUpdateGroupName $SoftwareUpdateGroupName 
-SoftwareUpdateId $UpdateID}
                        LogWrite("Added Updates to Software Update Group: 
$SoftwareUpdateGroupName")($true)
                }
                Else
                {
                        #Create Software Update Group
                        New-CMSoftwareUpdateGroup -Name 
$SoftwareUpdateGroupName -UpdateID $UpdateIDs | Out-Null
                        LogWrite ("Created Software Update Group")($true)
                        
                        #Create the deployments using the created Software 
Update group
                        $DeadLineDay = (Get-Date).AddDays(7 - 
([INT](Get-Date).DayofWeek)).ToString("yyyy/MM/dd") #Sets deadline for the next 
Sudany
                        $IgnoreDeadLineDay = (Get-Date).AddDays(14 - 
([INT](Get-Date).DayofWeek)).ToString("yyyy/MM/dd") #Sets deadline for the next 
Sudany
                        Start-CMSoftwareUpdateDeployment 
-SoftwareUpdateGroupName $SoftwareUpdateGroupName -CollectionName "Software 
Updates - Default" -DeploymentName "$SoftwareUpdateGroupName" -DeploymentType 
Required -VerbosityLevel OnlySuccessAndErrorMessages -TimeBasedOn LocalTime 
-DownloadFromMicrosoftUpdate $false -UnprotectedType NoInstall 
-DeploymentExpireDay $DeadLineDay -DeploymentExpireTime 1:00
                        Start-CMSoftwareUpdateDeployment 
-SoftwareUpdateGroupName $SoftwareUpdateGroupName -CollectionName "Software 
Updates - Ignore Maint Windows" -DeploymentName "$SoftwareUpdateGroupName 
(Ignore Maint Windows)" -DeploymentType Required -VerbosityLevel 
OnlySuccessAndErrorMessages -TimeBasedOn LocalTime -DownloadFromMicrosoftUpdate 
$false -UnprotectedType NoInstall -DeploymentExpireDay $IgnoreDeadLineDay 
-DeploymentExpireTime 1:00 -SoftwareInstallation $true -AllowRestart $true
                        LogWrite("Created Deployments for both Ignore and 
Comply Maintance Windows Collections")($true)
                }
        }
}

Function CheckPackageExists($SoftwareUpdateGroupName)
{
        $SUPackage = Get-WmiObject -ComputerName $CMServer -Namespace 
"root\sms\Site_$SiteCode" -Class SMS_SoftwareUpdatesPackage -Filter 
"Name='Software Updates - $SoftwareUpdateGroupName'"
        If (!($SUPackage)) 
        {
                Set-Location C:; Set-Location $ScriptDir
                New-Item -Path "\\$CMServer\SoftwareLibrary$\Software Updates" 
-Name $SoftwareUpdateGroupName -Type "directory" -Force | Out-Null
                Set-Location $SiteDrive
                
                $Arguments = @{Name = "Software Updates - 
$SoftwareUpdateGroupName"; Description = ""; PkgSourceFlag = 2; PkgSourcePath = 
"\\$CMServer\SoftwareLibrary$\Software Updates\$SoftwareUpdateGroupName"}
                Set-WmiInstance -ComputerName $CMServer -Namespace 
"root\sms\Site_$SiteCode" -Class "SMS_SoftwareUpdatesPackage" -Arguments 
$Arguments | Out-Null

                $SUPackage = Get-WmiObject -ComputerName $CMServer -Namespace 
"root\sms\Site_$SiteCode" -Class "SMS_SoftwareUpdatesPackage" -Filter 
"Name='Software Updates - $SoftwareUpdateGroupName'"
                $SUPackage = [wmi]$SUPackage.__PATH
                $SUPackage.PkgFlags = 16777216
                $SUPackage.put() | Out-Null
                
                LogWrite ("Created Package: Software Updates - 
$SoftwareUpdateGroupName ($($SUPackage.PackageID))")($true)
        }
        return $SUPackage
}       

Function DownloadIndividualUpdate($SoftwareUpdateName)
{
        LogWrite ("Downloading missed update: $SoftwareUpdateName")($true)

        $StartTime = Get-Date

        Set-CMSoftwareUpdateAutoDeploymentRule -Name $ADRName -Required ">=1" 
-Superseded $false -Title $SoftwareUpdateName -Force
        Invoke-CMSoftwareUpdateAutoDeploymentRule -Name $ADRName

        $DownloadDone = $false
        Do
        {
                $ADRLastRunTime = (Get-CMSoftwareUpdateAutoDeploymentRule -Name 
$ADRName).LastRunTime
                If ($ADRLastRunTime -gt $StartTime) {$DownloadDone = $true} 
Else {Start-Sleep -s 60}
        }
        Until ($DownloadDone -eq $true)

        Remove-CMSoftwareUpdateGroup -Name $ADRName -Force
}

#----------------------------------------Main Script Starts 
here-----------------------------------------------------------------------
$ScriptDir = split-path $SCRIPT:MyInvocation.MyCommand.Path -parent
$CMServer = ""; $SiteCode = ""; $SiteDrive = $SiteCode + ":"

If ($host.version.major -ne 3) {write-host "PowerShell 3 needs to be 
installed..."; return}
If ($Env:SMS_ADMIN_UI_PATH -eq $null) {write-host "ConfigMgr Console needs to 
be installed..."; return}

$ModulePath = 
$Env:SMS_ADMIN_UI_PATH.Replace(„\bin\i386“,“\bin\ConfigurationManager.psd1“)
Import-Module $ModulePath

If (!(Test-Path $SiteDrive)) {write-host "Launch ConfigMgr Console and connect 
to PowerShell..."; return}

$LogName = "$ScriptDir\DeploySoftwareUpdates.log"
write-output 
"------------------------------------------------------------------------------------------------------------------------"
 | Out-File -FilePath $LogName -Append -NoClobber -Encoding default

$UpdateNameStrings = @(); Get-Content 
"$ScriptDir\DeploySoftwareUpdateStrings.txt" | Foreach-Object 
{$UpdateNameStrings += $_}

$AllDPGroupID = (Get-WmiObject -ComputerName $CMServer -NameSpace 
"root\SMS\Site_$SiteCode" -Class "SMS_DPGroupInfo" -Filter "Name='All 
Distribution Points'").GroupID
$swbem = New-Object -ComObject wbemscripting.swbemdatetime
$ADRName = "Deploy Software Updates"

Set-Location $SiteDrive

Foreach ($SoftwareUpdateGroup in (Get-CMSoftwareUpdateGroup | Sort-Object 
LocalizedDisplayName))
{
        If ($($SoftwareUpdateGroup.LocalizedDisplayName).substring(0,2) -eq 
"20") 
        {
                If ($SoftwareUpdateGroup.LocalizedDisplayName -eq "$(Get-Date 
-format yyyy)-$(Get-Date -format MM)") {$CurrentMonthProcessed = $True}

                
ProcessSoftwareUpdateGroup($SoftwareUpdateGroup.LocalizedDisplayName)($true)
                LogWrite(" ")($false)
        }
}

If (!($CurrentMonthProcessed -eq $True)) 
{ProcessSoftwareUpdateGroup("$(Get-Date -format yyyy)-$(Get-Date -format 
MM)")($false)}

Set-Location C:; Set-Location $ScriptDir
Cumulative Security Update for ActiveX Killbits
Cumulative Security Update for Internet Explorer
Cumulative Update for Internet Explorer
Definition Update for Microsoft Office
Office 2003 Web Components Service Pack 1 for the 2007 Microsoft Office System
Rules Update for RRAS Best Practice Analyzer
Security Update for CAPICOM
Security Update for Internet Explorer
Security Update for Microsoft SharePoint Server 2013 Client Components SDK
Security Update for Microsoft Visual C++ 2010 Redistributable Package
System Update Readiness Tool
Update for Lync 2010
Update for .NET Native
Update for Microsoft .NET Framework
Update for Microsoft Access
Update for Microsoft Camera Codec Pack for Windows
Update for Microsoft Excel
Update for Microsoft Filter Pack 2.0
Update for Microsoft InfoPath
Update for Microsoft Lync
Update for Microsoft Office
Update for Microsoft OneDrive
Update for Microsoft OneNote
Update for Microsoft Outlook
Update for Microsoft PowerPoint
Update for Microsoft Project
Update for Microsoft Publisher
Update for Microsoft SharePoint Designer
Update for Microsoft SharePoint Workspace
Update for Microsoft Silverlight
Update for Microsoft SkyDrive Pro
Update for Microsoft Visio
Update for Microsoft Visual Studio
Update for Microsoft Word
Update for Microsoft XML Core Services
Update for Office
Update for Office File Validation
Update for Outlook
Update for Publisher
Update for Root Certificates
Update for the 2007 Microsoft Office System
Update for Windows
Update For Windows
Update for Word
Windows 8.1 Update for x64-based Systems
Windows Malicious Software Removal Tool
Windows Server 2012 R2 Update

Reply via email to