Scott,
After reviewing your script and modifying it a bit to suit our needs, I have a
question on how I can make something work. The background information has
changed, such that the need is for monthly IE updates in 1 package each month,
monthly OS updates in 1 package each month, and monthly Office updates in 1
package each month. The reason behind this is hundreds of pull DPs on slow
links and their timing out if the package hasn't finished transferring in an
allotted time, which usually happens with packages over 2-3 gigs.
Thus, as a POC, I modified the script to include "-IE" in the package name that
is created on the drive (so it's called 2014-11-IE), the Deployment Package is
"Software Updates-2014-11-IE", and the Software Update Group is "2014-11-IE."
This works once. However, when I run the script again (let's say it's now
December), this error is given:New-CMSoftwareUpdateGroup : An object with the
specified name already exists.
I presume this is because I appended "-IE" to several areas of the script.
Prior to me appending that, it worked fine and would iterate through without
error, regardless of month. However, I to also want to make two additional
copies of this with the appendage of "-OS" and "-Office". This will help me
separate the packages and update groups and determine which is which. I'm not
particularly concerned with it adding additional updates to previous months,
but do need it to run at least once a month on Patch Tuesday.
Are these things possible given the script you created? I've gone through the
script several times and tried different things, but I am far from a programmer
or PowerShell guru. I've attached my changed script for reference. If you have
a moment and can provide any help, I'd surely appreciate it!
Regards,-S
From: [email protected]
Subject: Re: [mssms] ADR and Dynamic Deployment Packages
Date: Wed, 29 Oct 2014 20:01:37 -0500
To: [email protected]
Thank you for this! It might take me a bit to test it out in our dev
environment as some other things have come up to take precedence recently. But
I'll definitely check it out further at the next available opportunity.
Regards,-S
On Oct 28, 2014, at 1:20 PM, Wilbers, Scott <[email protected]> wrote:
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]
To: [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]]
On Behalf Of s kissel
Sent: Monday, October 27, 2014 3:27 PM
To: [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.
**************************************************************************************************
<DeploySoftwareUpdates.ps1.txt><DeploySoftwareUpdateStrings.txt>
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)
Foreach ($SoftwareUpdate in (Get-WmiObject -ComputerName $CMServer
-NameSpace "root\SMS\Site_$SiteCode" -Class "SMS_SoftwareUpdate" -Filter
"IsSuperseded=0 AND IsExpired=0 AND LocalizedDisplayName NOT LIKE '%Itanium%'")
| 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
Set-CMSoftwareUpdateAutoDeploymentRule -Name $ADRName -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 "North America
Distribution Points"; LogWrite ("Content sent to North America 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 - Test - IE - Patch Tuesday" -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 Deployment")($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 "\\*****\Packages$\SoftwareUpdates\IE" -Name
$SoftwareUpdateGroupName -Type "directory" -Force | Out-Null
Set-Location $SiteDrive
$Arguments = @{Name = "Software
Updates-$SoftwareUpdateGroupName"; Description = ""; PkgSourceFlag = 2;
PkgSourcePath = "\\*****\Packages$\SoftwareUpdates\IE\$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
Set-CMSoftwareUpdateAutoDeploymentRule -Name $ADRName -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-IE.log"
write-output
"------------------------------------------------------------------------------------------------------------------------"
| Out-File -FilePath $LogName -Append -NoClobber -Encoding default
$UpdateNameStrings = @(); Get-Content
"$ScriptDir\DeploySoftwareUpdateStrings-IE.txt" | Foreach-Object
{$UpdateNameStrings += $_}
$AllDPGroupID = (Get-WmiObject -ComputerName $CMServer -NameSpace
"root\SMS\Site_$SiteCode" -Class "SMS_DPGroupInfo" -Filter "Name='North America
Distribution Points'").GroupID
$swbem = New-Object -ComObject wbemscripting.swbemdatetime
$ADRName = "Testing - Patch Tuesday IE Updates - One Package Per Month"
Set-Location $SiteDrive
Foreach ($SoftwareUpdateGroup in (Get-CMSoftwareUpdateGroup | Sort-Object
LocalizedDisplayName))
{
If ($($SoftwareUpdateGroup.LocalizedDisplayName).substring(0,5) -eq
"20-IE")
{
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)-IE")($false)}
Set-Location C:; Set-Location $ScriptDir