Saturday, 7 August 2021

Move Software Updates between Software Update Groups in SCCM using Powershell

How does the Powershell script work?
If you want to move a Software Update between different Software Update Groups, you don’t move them, but rather change the membership.

The script that I have provided will take the latest created Software Update Group and add those members to a defined Software Update Group.

The Powershell script
Below is the script I have created for editing the membership of a Software Update Group.

<#
.SYNOPSIS
This script will add all updates from the latest Software Update Group to a defined Software Update Group.
    
.DESCRIPTION
This script will add all updates from the latest Software Update Group to a defined Software Update Group.

Author: Daniel Classon (www.danielclasson.com)
Version: 1.0
Date: 2018-12-18
    
.EXAMPLE
.\Add-SUGMembers -DestinationSUG SUG1
This will add all updates contained in the latest Software Updates Group to SUG1.
    
.DISCLAIMER
All scripts and other powershell references are offered AS IS with no warranty.
These script and functions are tested in my environment and it is recommended that you test these scripts in a test environment before using in your production environment.
#>


PARAM(
    [string]$DestinationSUG
    )

#Load SCCM Powershell module
Try {
    Import-Module (Join-Path $(Split-Path $env:SMS_ADMIN_UI_PATH) ConfigurationManager.psd1) -ErrorAction Stop
}
Catch [System.UnauthorizedAccessException] {
    Write-Warning -Message "Access denied" ; break
}
Catch [System.Exception] {
    Write-Warning "Unable to load the Configuration Manager Powershell module from $env:SMS_ADMIN_UI_PATH" ; break
}

#Set current drive to SCCM drive

$SiteCode = Get-PSDrive -PSProvider CMSITE
Set-Location -Path "$($SiteCode.Name):\"

Function Write-Log
{
 
    PARAM(
        [String]$Message,
        [int]$Severity,
        [string]$Component
    )
        $LogPath = $PSScriptRoot
        $TimeZoneBias = Get-WMIObject -Query "Select Bias from Win32_TimeZone"
        $Date= Get-Date -Format "HH:mm:ss.fff"
        $Date2= Get-Date -Format "MM-dd-yyyy"
        $Type=1
         
        "<![LOG[$Message]LOG]!><time=$([char]34)$Date$($TimeZoneBias.bias)$([char]34) date=$([char]34)$date2$([char]34) component=$([char]34)$Component$([char]34) context=$([char]34)$([char]34) type=$([char]34)$Severity$([char]34) thread=$([char]34)$([char]34) file=$([char]34)$([char]34)>"| Out-File -FilePath "$LogPath\Add-SUGMembers.log" -Append -NoClobber -Encoding default
}

#Change membership of Software Update to new Software Update Group

$LatestSUG = Get-CMSoftwareUpdateGroup | Select-Object LocalizedDisplayName,DateCreated | Sort-Object DateCreated | Select-Object -Last 1 
$Updates = Get-CMSoftwareUpdate -UpdateGroupName $LatestSUG -Fast
$Count = 0

Foreach ($Update in $Updates) {
    $Count++
    Try {
        Add-CMSoftwareUpdateToGroup -SoftwareUpdateId $($Update.CI_ID) -SoftwareUpdateGroupId $($DestinationSUG.CI_ID)
        Write-Progress -Activity "Adding $($Update.LocalizedDisplayName) to $($DestinationSUG.LocalizedDisplayName)" -Status "Adding $Count of $($Updates.Count) updates to $($SourceSUG.LocalizedDisplayName)" -PercentComplete ($Count / $Updates.count * 100)
        Write-Log -Message "Adding $($Update.LocalizedDisplayName) to $($DestinationSUG.LocalizedDisplayName)" -Severity 1 -Component "Add SU to SUG"
    }
    Catch {
        Write Warning "$_.Exception.Message"
        Write-Log -Message "$_.Exception.Message" -Severity 3 -Component "Add SU to SUG"
    }
}
Set-Location $PSScriptRoot


Share:

Configure Collection Updates in MEMCM (SCCM) using a Powershell Script

Configure Collection Updates in MEMCM (SCCM) using a Powershell Script
If you want to configure device collections in collections.txt to only update on a schedule, you run this command:

.\Set-CollectionUpdates.ps1 -CollectionType Device -RefreshType 2
Source code:
<#
.PARAMETER COLLECTIONTYPE
Define the type of collections in the collections.txt file. Valid inputs are Device and User

.PARAMETER REFRESHTYPE
Define the Refresh type for the collection. Valid inputs are:

# The following refresh types exist for ConfigMgr collections 
# 6 = Incremental and Scheduled Updates 
# 4 = Incremental Updates Only 
# 2 = Scheduled Updates only 
# 1 = Manual Update only 
    
.DESCRIPTION
Sets the collection refresh type for all collections defined in a text file.

.NOTES
Author: Daniel Classon
Version: 1.0
Date: 2015/05/18
    
.EXAMPLE
.\Set-Collection_Updates.ps1 -CollectionType Device -RefreshType 2
Will set the collections in collections.txt to "Scheduled Updates only".

.DISCLAIMER
All scripts and other powershell references are offered AS IS with no warranty.
These script and functions are tested in my environment and it is recommended that you test these scripts in a test environment before using in your production environment.
#>

[CmdletBinding()]

Param(
    [Parameter(Mandatory=$True, Helpmessage="Enter the Collection Type")]
    [string]$CollectionType,
    [Parameter(Mandatory=$True, Helpmessage="Enter the Refresh Type")]
    [string]$RefreshType
)

Begin {
    #Checks if the user is in the administrator group. Warns and stops if the user is not.
    if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
        Write-Warning "You are not running this as local administrator. Run it again in an elevated prompt." ; break
    }
    try {
        Import-Module (Join-Path $(Split-Path $env:SMS_ADMIN_UI_PATH) ConfigurationManager.psd1)
    }
    catch [System.UnauthorizedAccessException] {
        Write-Warning -Message "Access denied" ; break
    }
    catch [System.Exception] {
        Write-Warning "Unable to load the Configuration Manager Powershell module from $env:SMS_ADMIN_UI_PATH" ; break
    }
}
Process {
    try {
        $CollectionIDs = Get-Content "collections.txt" -ErrorAction Stop
    }
    catch [System.Exception] {
        Write-Warning "Unable to find collections.txt. Make sure to place it in the script directory."
    }
    $SiteCode = Get-PSDrive -PSProvider CMSITE
    Set-Location -Path "$($SiteCode.Name):\"
    
    $Count = 0

    Foreach ($CollectionID in $CollectionIDs) {
        $Count++
        if ($CollectionType -eq "Device") {
            $Collection = Get-CMDeviceCollection -CollectionId $CollectionID
            $Collection.RefreshType = $RefreshType
            $Collection.Put()
            if ($RefreshType -eq 1) {
                Write-Progress -Activity "Enabling Manual Update only  on $CollectionType Collection ID: $CollectionID" -Status "Modified $Count of $($CollectionIDs.Count) collections" -PercentComplete ($Count / $CollectionIDs.count * 100)
                Write-Host "Enabling Manual Update only on: " $Collection.CollectionID "`t" $Collection.Name -ForegroundColor Yellow
            }
            elseif ($RefreshType -eq 2) {
                Write-Progress -Activity "Enabling Scheduled Updates only on $CollectionType Collection ID: $CollectionID" -Status "Modified $Count of $($CollectionIDs.Count) collections" -PercentComplete ($Count / $CollectionIDs.count * 100)
                Write-Host "Enabling Scheduled Updates only on: " $Collection.CollectionID "`t" $Collection.Name -ForegroundColor Yellow
            }
            elseif ($RefreshType -eq 4) {
                Write-Progress -Activity "Enabling Incremental Updates only on $CollectionType Collection ID: $CollectionID" -Status "Modified $Count of $($CollectionIDs.Count) collections" -PercentComplete ($Count / $CollectionIDs.count * 100)
                Write-Host "Enabling Incremental Updates Only on: " $Collection.CollectionID "`t" $Collection.Name -ForegroundColor Yellow
            }
            elseif ($RefreshType -eq 6) {
                Write-Progress -Activity "Enabling Incremental and Scheduled Updates on $CollectionType Collection ID: $CollectionID" -Status "Modified $Count of $($CollectionIDs.Count) collections" -PercentComplete ($Count / $CollectionIDs.count * 100)
                Write-Host "Enabling Incremental and Scheduled Updates on: " $Collection.CollectionID "`t" $Collection.Name -ForegroundColor Yellow
        }
        } 
        if ($CollectionType -eq "User") {
            $Collection = Get-CMUserCollection -CollectionId $CollectionID
            $Collection.RefreshType = $RefreshType
            $Collection.Put()
            if ($RefreshType -eq 1) {
                Write-Progress -Activity "Enabling Manual Update only  on $CollectionType Collection ID: $CollectionID" -Status "Modified $Count of $($CollectionIDs.Count) collections" -PercentComplete ($Count / $CollectionIDs.count * 100)
                Write-Host "Enabling Manual Update only on: " $Collection.CollectionID "`t" $Collection.Name -ForegroundColor Yellow
            }
            elseif ($RefreshType -eq 2) {
                Write-Progress -Activity "Enabling Scheduled Updates only on $CollectionType Collection ID: $CollectionID" -Status "Modified $Count of $($CollectionIDs.Count) collections" -PercentComplete ($Count / $CollectionIDs.count * 100)
                Write-Host "Enabling Scheduled Updates only on: " $Collection.CollectionID "`t" $Collection.Name -ForegroundColor Yellow
            }
            elseif ($RefreshType -eq 4) {
                Write-Progress -Activity "Enabling Incremental Updates only on $CollectionType Collection ID: $CollectionID" -Status "Modified $Count of $($CollectionIDs.Count) collections" -PercentComplete ($Count / $CollectionIDs.count * 100)
                Write-Host "Enabling Incremental Updates Only on: " $Collection.CollectionID "`t" $Collection.Name -ForegroundColor Yellow
            }
            elseif ($RefreshType -eq 6) {
                Write-Progress -Activity "Enabling Incremental and Scheduled Updates on $CollectionType Collection ID: $CollectionID" -Status "Modified $Count of $($CollectionIDs.Count) collections" -PercentComplete ($Count / $CollectionIDs.count * 100)
                Write-Host "Enabling Incremental and Scheduled Updates on: " $Collection.CollectionID "`t" $Collection.Name -ForegroundColor Yellow
        }
        } 
} 
}  
End {
    Write-Host "$Count $CollectionType collections were updated"  
    Set-Location -Path $env:SystemDrive
}
Share:

Custom WQL query to include or exclude

Use the following example queries and replace the example collection ID XYZ0003F with the ID of the collection you want to include or exclude.


Include:

Select * from SMS_R_System where SMS_R_System.ResourceId in (select ResourceID from SMS_CM_RES_COLL_XYZ0003F)

Exclude:

Select * from SMS_R_System where SMS_R_System.ResourceId not in (select ResourceID from SMS_CM_RES_COLL_XYZ0003F)

Share:

SQL Querrey to monitor collection evaluation

Monitor collection evaluation

SELECT [t2].[CollectionName], [t2].[SiteID], [t2].[value] AS [Seconds], [t2].[LastIncrementalRefreshTime], [t2].[IncrementalMemberChanges] AS [IncChanges], [t2].[LastMemberChangeTime] AS [MemberChangeTime]
FROM (
    SELECT [t0].[CollectionName], [t0].[SiteID], DATEDIFF(Millisecond, [t1].[IncrementalEvaluationStartTime], [t1].[LastIncrementalRefreshTime]) * 0.001 AS [value], [t1].[LastIncrementalRefreshTime], [t1].[IncrementalMemberChanges], [t1].[LastMemberChangeTime], [t1].[IncrementalEvaluationStartTime], v1.[RefreshType]
    FROM [dbo].[Collections_G] AS [t0]
    INNER JOIN [dbo].[Collections_L] AS [t1] ON [t0].[CollectionID] = [t1].[CollectionID]
    inner join v_Collection v1 on [t0].[siteid] = v1.CollectionID
    ) AS [t2]
WHERE ([t2].[IncrementalEvaluationStartTime] IS NOT NULL) AND ([t2].[LastIncrementalRefreshTime] IS NOT NULL) and (refreshtype='4' or refreshtype='6')
ORDER BY [t2].[value] DESC
Share:

Powershell Application detection method example

Powershell detection method example
Modify the Powershell script

Modify the below script to suit your needs. The example below will look at strings in 3 different files. You will need to make the following modifications:

  • String variables
  • String<number>Location variables

#Define strings and their location. Also include the filename.

$String1 = ""
$String1Location = ""

$String2 = ""
$String2Location = ""

$String3 = ""
$String3Location = ""

# Detect presence of String1 in String1 Location
try {
    $String1Exists = Get-Content $String1Location -ErrorAction Stop
}
catch {
}

# Detect presence of String2 in String2 Location
try {
    $String2Exists = Get-Content $String2Location -ErrorAction Stop
}
catch {
}

# Detect presence of String3 in String3 Location
try {
    $String3Exists = Get-Content $String3Location -ErrorAction Stop
}
catch {
}

if (($String1Exists -match $String1) -and ($String2Exists -match $String2) -and ($String3Exists -match $String3)) {
    Write-Host "Installed"
}
else {
}
Share: