Get ConfigMgr Collection rules

I’m in the process of installing Forefront Endpoint Protection and wanted to look at some of the collection queries that was created… but with the ConfigMgr console you cant view them…

So Powershell it is.

Did a function that you can use on any collection (with subcollections) to view the WQL.

Import-Module SCCM\SCCM-Functions -Force
Function Get-CollectionRules {
        PARAM (
                $parentCollection,
                $spacer,
                $sccm
        )

        $subCollections = Get-SCCMSubCollections -SccmServer $sccm -CollectionID $parentCollection

        if ($subCollections -ne $null) {
                $subCollections | ForEach-Object {
                        $collection = Get-SCCMCollection -Filter "CollectionID='$($_.subCollectionID)'" -SccmServer $sccm
                        Write-Host "$($spacer) Name: " -ForegroundColor Yellow -NoNewline
                        Write-Host "$($collection.CollectionID) - $($collection.Name)"

                        $collectionRule = (Get-SCCMCollectionRules -SccmServer ( Connect-SCCMServer ) -CollectionID $collection.CollectionID)
                        if ($collectionRule -ne $null) {
                                Write-Host "$($spacer)Limit: " -ForegroundColor Yellow -NoNewline
                                if ($collectionRule.LimitToCollectionID.Length -gt 0) {
                                        Write-Host "$($collectionRule.LimitToCollectionID)" -ForegroundColor White
                                } else {
                                        Write-Host "" -ForegroundColor Gray
                                }

                                Write-Host "$($spacer)  WQL: " -ForegroundColor Yellow -NoNewline
                                Write-Host "$($collectionRule.QueryExpression)"
                        } else {
                                Write-Host "$($spacer)" -ForegroundColor Gray
                        }
                        Write-Host ""

                        Get-CollectionRules -parentCollection $_.subCollectionID -spacer "   $($spacer)" -sccm $sccm
                }
        }
}

Get-CollectionRules -parentCollection "XYZ00123" -spacer "" -sccm (Connect-SCCMServer)

A small warning: It will loop all of the subcollections, and the subcollections subcollections, and so on…


Bulk import of SNMP devices to OpsMgr

If you want to import a larger bunch of SNMP-devices in to OpsMgr you will probably go thru the disovery wizard way to many times.

Instead of walking thru that wizard every time I asked the network team to write a CSV-file with all the devices and then used this function to import them.

Function Add-SnmpDevice {
        PARAM (
                [Parameter(Mandatory=$true )][string] $FromIpAddress,
                [Parameter(Mandatory=$true )][string] $MonitoringProxy,
                [string] $ManagementServer,
                [string] $ToIpAddress = "",
                [string] $SnmpCommunityString = "public",
                [int32] $SnmpVersion = 2
        )

        # Single ip ?
        If ($ToIpAddress.Length -eq 0) {
                $ToIpAddress = $FromIpAddress
        }

        # Check SNMP version
        if ($SnmpVersion -ne 1 -and $SnmpVersion -ne 2) {
                Throw "Only SNMP version 1 and 2 supported"
        }

        Write-Host "Setting up discovery for SNMP-devices..."
        Write-Host "       From: $($FromIpAddress)"
        Write-Host "         To: $($ToIpAddress)"
        Write-Host "  Community: $($SnmpCommunityString)"
        Write-Host "   SNMP ver: $($SnmpVersion)"

        $networkDeviceClass = Get-MonitoringClass -name "System.NetworkDevice"
        $DeviceDiscoveryConfig = New-DeviceDiscoveryConfiguration -MonitoringClass $networkDeviceClass -FromIpAddress $FromIpAddress -ToIpAddress $ToIpAddress

        # Set Community String
        $encoding = New-Object System.Text.UnicodeEncoding
        $encodedCommunityString = $encoding.GetBytes($SnmpCommunityString)
        $DeviceDiscoveryConfig.ReadOnlyCommunity = [System.Convert]::ToBase64String($encodedCommunityString)

        # Set SNMP version
        $DeviceDiscoveryConfig.SnmpVersion = $SnmpVersion

        # Get management server
        If ($ManagementServer.Length -eq 0) {
                $mgmtServer = Get-RootManagementServer
        } else {
                $mgmtServer = Get-ManagementServer | Where-Object {$_.Name -eq $ManagementServer}
        }
        If ($mgmtServer -eq $null) {
                Throw "Cant find management server named $($ManagementServer)"
        } else {
                Write-Host "Found management server: $($mgmtServer.name)"
        }

        # Find proxy agent
        Write-Host "Lookup of proxy agent named $($MonitoringProxy) ..."
        $ProxyAgent = Get-Agent | Where-Object {$_.Name -eq $MonitoringProxy}
        If ($ProxyAgent -eq $null) {
                Write-Host "No agent named $($MonitoringProxy) found, checking managementservers"
                $ProxyAgent = Get-ManagementServer | Where-Object {$_.Name -eq $MonitoringProxy}
                $ProxyIsMgmtServer = $true
        } else {
                $ProxyIsMgmtServer = $false
        }

        If ($ProxyAgent -eq $null) {
                Throw "Can't find agent or managementserver named $($MonitoringProxy)"
        } else {
                Write-Host "Found $($ProxyAgent.Name)"
        }

        Write-Host "Starting discovery..."
        $DiscResults = Start-Discovery -ManagementServer: $mgmtServer -DeviceDiscoveryConfiguration: $DeviceDiscoveryConfig

        If ($DiscResults.CustomMonitoringObjects.Count -eq 0) {
                Write-Host "Cant discover any objects"
                Return 0
        } else {
                $ObjectCount = 0
                Write-Host "Found objects"
                $discresults | select-object -expandproperty CustomMonitoringObjects | Select-Object Name | Format-Table -HideTableHeaders

                $DiscResults | ForEach-Object {
                        Write-Host "Adding object to proxy..."
                        if ($ProxyIsMgmtServer -eq $true) {
                                $ProxyAgent.InsertRemotelyManagedDevices($_.CustomMonitoringObjects) | Format-Table SnmpDevice, ProxyAgentPrincipalName, ManagementGroup -HideTableHeaders
                        } else {
                                Add-RemotelyManagedDevice -proxyagent $ProxyAgent -device $_.CustomMonitoringObjects
                        }
                        $ObjectCount++
                }
                Return $ObjectCount
        }
}

And two examples on how you can use it to add some devices:

# Add a devices in the range 192.168.100.240-.254 with the community "SomeSecret"
# Use mgmtserver.snowland.demo to do the discovery and add the devices with snmpmonitor.snowland.demo as monitoring proxy
Add-SnmpDevice -FromIpAddress "192.168.100.240" -ToIpAddress "192.168.100.254" -SnmpCommunityString "SomeSecret" -ManagementServer "mgmtserver.snowland.demo" -MonitoringProxy "snmpmonitor.snowland.demo"

# Add a single SNMPv1 device with the "public" community, use the RMS to do discovery
Add-SnmpDevice -FromIpAddress "192.168.100.10" -MonitoringProxy "snmpmonitor.snowland.demo" -SnmpVersion 1

So we ended up with something like this:

Import-Csv ".\snmplist.csv" -Delimiter ";" | ForEach-Object {
        Add-SnmpDevice -FromIpAddress $_.IpFrom -ToIpAddress $_.IpTo -SnmpCommunityString $_.Community -ManagementServer $_.MgmtServer -MonitoringProxy $_.Proxy
}

Change Source-paths in ConfigMgr

I’m in the process of moving tons of packages to a new source.

So… I did a few new functions to my Powershell Module https://snowland.se/sccm-posh/ :-)

Update-SCCMDriverPkgSourcePath -sccmserver (Connect-SCCMServer) -currentPath "\\OLDSERVER\Source\DriverPackages" -newPath "\\NEWSERVER\Source\DriverPackages"
Update-SCCMPackageSourcePath -sccmserver (Connect-SCCMServer) -currentPath "\\OLDSERVER\Source\Packages" -newPath "\\NEWSERVER\Source\Packages"
Update-SCCMDriverSourcePath -sccmserver (Connect-SCCMServer) -currentPath "\\OLDSERVER\Source\DriverImport" -newPath "\\NEWSERVER\Source\DriverImport"

Oh… and I found some additional updates posted in German by Stefan Ringler … I don’t understand that many words of German, but I can read Powershell. :-P

Anyway, I included the updates in the module… thanks Stefan for sharing.


Maintenance Mode via Powershell Remoting

There are loads of scripts and GUIs that you can use to set Maintenance Mode in OpsMgr, but if you want to do this from a server that doesn’t have the OpsMgr-snapins for Powershell it’s a bit harder…

But then there is Powershell v2 and Remoting… It gives you the option to run a scriptblock on another computer…

Just enable remoting on your RMS and then try this script from another machine:

Function setMaintMode {
        PARAM (
                [string] $rmsHostname,
                [string] $agentName,
                [string] $Comment,
                [string] $Reason,
                [int] $Time
        )

        Invoke-Command -ComputerName $rmsHostname -scriptblock {
                PARAM (
                        [string] $agentName,
                        [string] $Comment,
                        [string] $Reason,
                        [int] $Time
                )

                Add-PSSnapin "Microsoft.EnterpriseManagement.OperationsManager.Client"
                Set-Location "OperationsManagerMonitoring::"
                New-ManagementGroupConnection -ConnectionString:localhost | Out-Null

                $computerClass = Get-MonitoringClass -name:Microsoft.Windows.Computer
                $computerCriteria = "PrincipalName='" + $agentName + "'"
                $computer = get-monitoringobject -monitoringclass:$computerClass -criteria:$computerCriteria

                if ($computer.InMaintenanceMode -eq $false) {
                        $startTime = [System.DateTime]::Now
                        $endTime = $startTime.AddMinutes($Time)

                        New-MaintenanceWindow -startTime $startTime -endTime $endTime -Comment $comment -Reason $Reason -monitoringObject $computer
                        return $true
                }  else {
                        # Allready in maintenance mode
                        return $false
                }

        } -ArgumentList $agentName, $Comment, $Reason, $Time
}

setMaintMode -rmsHostname "rmsserver.domain.local" -agentName "currentserver.domain.local" -Comment "Some comment" -Time 30 -Reason "PlannedOperatingSystemReconfiguration"

What it does is that it run’s the OpsMgr-specific parts on the RMS instead on your local machine… so with that in place it’s easy to create a GUI around it and then spread a shortcut to all your servers that have Powershell v2 installed.

Notes:
The quick and dirty way to enable remoting on your rms, start cmd as an administrator and run winrm quickconfig
Here can you find a quick intro to PS Remoting.


Change a folder LastWriteTime based on files within it

A few days ago I wrote a script that copies some files. Did notice that everything except the date on the folders were ok. So I added a few more lines of powershell code.

Did find a few suggestions on the web, but I like this one…. Since I wrote it. :-P

Function Set-FolderDate {
        Param (
                [string] $Path
        )
        Trap  [Exception] {
                Write-Debug $("TRAPPED: " + $_.Exception.Message);
                Write-Verbose "Could not change date on folder (Folder open in explorer?)"
                Continue
        }

        # Get latest filedate in folder
        $LatestFile = Get-ChildItem $Path | Sort-Object LastWriteTime -Descending | Select-Object -First 1

        # Change the date, if needed
        $Folder = Get-Item $path
        if ($LatestFile.LastWriteTime -ne $Folder.LastWriteTime) {
                Write-Verbose "Changing date on folder '$($Path)' to '$($LatestFile.LastWriteTime)' taken from '$($LatestFile)'"
                $Folder.LastWriteTime = $LatestFile.LastWriteTime
        }

        Return $Folder
}

Set-FolderDate -Path "D:\temp"

Unzip multiple files via Powershell

Simple and effective.

PARAM (
        [string] $ZipFilesPath = "X:\Somepath\Full\Of\Zipfiles",
        [string] $UnzipPath = "X:\Somepath\to\extract\to"
)

$Shell = New-Object -com Shell.Application
$Location = $Shell.NameSpace($UnzipPath)

$ZipFiles = Get-Childitem $ZipFilesPath -Recurse -Include *.ZIP

$progress = 1
foreach ($ZipFile in $ZipFiles) {
        Write-Progress -Activity "Unzipping to $($UnzipPath)" -PercentComplete (($progress / ($ZipFiles.Count + 1)) * 100) -CurrentOperation $ZipFile.FullName -Status "File $($Progress) of $($ZipFiles.Count)"
        $ZipFolder = $Shell.NameSpace($ZipFile.fullname)

        $Location.Copyhere($ZipFolder.items(), 1040) # 1040 - No msgboxes to the user - http://msdn.microsoft.com/en-us/library/bb787866%28VS.85%29.aspx
        $progress++
}

Btw… Watch out… there is a -Recurse on gci… :-)


ACT filling up your disks?

When you have ACT (The Microsoft Application Compatibility Toolkit) running in your environment you might get disks filled up with XML-files on your ACT-server.

The logprocessor uses those files to populate the database with information. So, when those are processed you do not need them anymore. (If you have a backup of your database…)

So, I did it the easy way, scheduled a powershell command to remove files older than 7 days:

Get-ChildItem D:\ACTLogs -Recurse -Include *.xml* | where {$_.CreationTime -lt (Get-Date).AddDays(-7)} | Remove-Item

If you want a nicer look and feel to your script you can use this instead:

Get-ChildItem D:\ACTLogs -Recurse -Include *.xml* | where {$_.CreationTime -lt (Get-Date).AddDays(-7)} | Sort-Object CreationTime | ForEach-Object {
        Write-Host "Processing: " -ForegroundColor Yellow -NoNewline
        Write-Host $_.FullName -ForegroundColor White -NoNewline

        $span = New-TimeSpan $_.CreationTime $(get-date)
        Write-Host " $($span.Days) days old" -ForegroundColor Yellow -NoNewline

        Remove-Item $_.FullName

        Write-Host " [del]" -ForegroundColor Red
}

Manufacturer / Model Collections with hierarchy

The last post made a flat structure of collections with “Manufacturer – Model”, in this post the script creates a hierarchy with (almost) the same collections.


The Model-collections queries are limited to the parent Manufacturer-collection.

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#                                                                                                                            Rikard Ronnkvist / snowland.se
#  Usage:
#   Download and install https://snowland.se/sccm-posh/
#   Save the file as CreateMM-collections-Hierarchy.ps1
#   PS:>.\CreateMM-collections-Hierarchy.ps1 -rootCollectionName "Name Of Some Collection"
#
#  2010-03-24   Rikard Ronnkvist    First snowland.se release
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PARAM (
        [string] $rootCollectionName = $(throw "rootCollectionName required."),
        [string] $hostName = (Get-Content env:computername),
        [switch] $Verbose,
        [Switch] $WhatIf
)

if ($verbose.IsPresent) {
        $VerbosePreference = 'Continue'
} Else {
        $VerbosePreference = 'SilentlyContinue'
}

Import-Module SCCM\SCCM-Functions -Force

Write-Verbose "Connect to SCCM-server $($hostName)"
$sccm = Connect-SCCMServer -HostName $hostName

Write-Host "Get root collection: ""$($rootCollectionName)"""
$rootCollection = Get-SCCMCollection -filter "Name='$($rootCollectionName)'" -sccmserver $sccm
if (!$rootCollection) {
        throw "Cant find ""$($rootCollectionName)"""
}
Write-Host "Found collection: $($rootCollection.CollectionID)"

Function checkAndCreate ($CollectionName, $ParentCollectionID, $wql, $limit = $null) {
        Write-Host "Checking ""$($CollectionName)""" -ForegroundColor Cyan
        $newCollection = Get-SCCMCollection -filter "Name='$($CollectionName)'" -sccmserver $sccm

        if (!$newCollection) {
                if (!$WhatIf.IsPresent) {
                        Write-Host "Creating collection: ""$($CollectionName)"""
                        $newCollection = New-SCCMCollection -name "$($CollectionName)" -SccmServer $sccm -parentCollectionID $ParentCollectionID -refreshDays 1 -Verbose
                } else {
                        Write-Host "What if: Creating collection: ""$($CollectionName)""" -ForegroundColor Red
                }

                if (!$WhatIf.IsPresent) {
                        Write-Verbose "Adding rule with WQL: $wql"
                        Add-SCCMCollectionRule -queryExpression $wql -Server $sccm -collectionID $newCollection.CollectionId -queryRuleName $CollectionName -limitToCollectionId $limit
                } else {
                        Write-Host "What if: Adding collection rule to new collection with wql: $($wql)" -ForegroundColor Red
                }
        } else {
                Write-Host "Found collection ""$($CollectionName)"""
        }

        return $newCollection
}

Write-Host "Lookup Manufacturer and Model"
$Manufacturer = Get-wmiobject -query "SELECT DISTINCT Manufacturer FROM SMS_G_System_COMPUTER_SYSTEM" -computername $Sccm.Machine -namespace $Sccm.Namespace | Sort-Object Manufacturer, Model
$Manufacturer | ForEach-Object {
        $wql = "SELECT * FROM SMS_R_System inner join SMS_G_System_COMPUTER_SYSTEM on SMS_G_System_COMPUTER_SYSTEM.ResourceId = SMS_R_System.ResourceId where SMS_G_System_COMPUTER_SYSTEM.Manufacturer = '$($_.Manufacturer)'"
        $ManufacturerCollection = checkAndCreate -collectionName $_.Manufacturer -ParentCollectionID $rootCollection.CollectionId -wql $wql -limit $null

        $Model = Get-wmiobject -query "SELECT DISTINCT Model FROM SMS_G_System_COMPUTER_SYSTEM WHERE Manufacturer = '$($ManufacturerCollection.Name)'" -computername $Sccm.Machine -namespace $Sccm.Namespace | Sort-Object Manufacturer, Model
        $Model | ForEach-Object {
                $wql = "SELECT * FROM SMS_R_System inner join SMS_G_System_COMPUTER_SYSTEM on SMS_G_System_COMPUTER_SYSTEM.ResourceId = SMS_R_System.ResourceId where SMS_G_System_COMPUTER_SYSTEM.Model = '$($_.Model)'"
                $ModelCollection = checkAndCreate -collectionName $_.Model -ParentCollectionID $ManufacturerCollection.CollectionId -wql $wql -limit $ManufacturerCollection.CollectionId
        }
}

Manufacturer / Model Collections

You have probably created one or two collections that points to a specific Manufacturer and/or Model.

Well, this script will look in to your SCCM-database and create that kind of collections for you.

First you need my SCCM Module for PowerShell
Then I created a collection named “000 – Manufacturer – Model”
Copy and paste the code below to a file, save it as CreateMM-collections.ps1
Run with at least one param, -rootCollectionName
Example: .\CreateMM-collections.ps1 -rootCollectionName “000 – Manufacturer – Model”

The script support the -WhatIf and -Verbose parameters… might be good to have when testing.

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#                                                                                                                            Rikard Ronnkvist / snowland.se
#  Usage:
#   Download and install https://snowland.se/sccm-posh/
#   Save the file as CreateMM-collections.ps1
#   PS:>.\CreateMM-collections.ps1 -rootCollectionName "Name Of Some Collection"
#
#  2010-03-23   Rikard Ronnkvist    First snowland.se release
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PARAM (
        [string] $rootCollectionName = $(throw "rootCollectionName required."),
        [string] $hostName = (Get-Content env:computername),
        [switch] $Verbose,
        [Switch] $WhatIf
)

if ($verbose.IsPresent) {
        $VerbosePreference = 'Continue'
} Else {
        $VerbosePreference = 'SilentlyContinue'
}

Import-Module SCCM\SCCM-Functions -Force

Write-Verbose "Connect to SCCM-server $($hostName)"
$sccm = Connect-SCCMServer -HostName $hostName

Write-Host "Get root collection: ""$($rootCollectionName)"""
$rootCollection = Get-SCCMCollection -filter "Name='$($rootCollectionName)'" -sccmserver $sccm
if (!$rootCollection) {
        throw "Cant find ""$($rootCollectionName)"""
}
Write-Host "Found collection: $($rootCollection.CollectionID)"

Write-Host "Lookup Manufacturer and Model"
$ManufacturerModel = Get-wmiobject -query "SELECT DISTINCT Manufacturer, Model FROM SMS_G_System_COMPUTER_SYSTEM" -computername $Sccm.Machine -namespace $Sccm.Namespace | Sort-Object Manufacturer, Model
$ManufacturerModel | ForEach-Object {
        $mmCollectionName = "$($_.Manufacturer) - $($_.Model)"
        Write-Host "Checking ""$($mmCollectionName)""" -ForegroundColor Cyan

        $mmCollection = Get-SCCMCollection -filter "Name='$($mmCollectionName)'" -sccmserver $sccm

        if (!$mmCollection) {
                if (!$WhatIf.IsPresent) {
                        Write-Host "Creating collection: ""$($mmCollectionName)"""
                        $newMmCollection = New-SCCMCollection -name "$($mmCollectionName)" -SccmServer $sccm -parentCollectionID $rootCollection.CollectionID -refreshDays 1
                } else {
                        Write-Host "What if: Creating collection: ""$($mmCollectionName)""" -ForegroundColor Red
                }

                $wql = "SELECT * FROM SMS_R_System inner join SMS_G_System_COMPUTER_SYSTEM on SMS_G_System_COMPUTER_SYSTEM.ResourceId = SMS_R_System.ResourceId where SMS_G_System_COMPUTER_SYSTEM.Manufacturer = '$($_.Manufacturer)'  AND SMS_G_System_COMPUTER_SYSTEM.Model = '$($_.Model)'"
                if (!$WhatIf.IsPresent) {
                        Write-Verbose "Adding rule with WQL: $wql"
                        Add-SCCMCollectionRule -queryExpression $wql -Server $sccm -collectionID $newMmCollection.CollectionId -queryRuleName $mmCollectionName
                } else {
                        Write-Host "What if: Adding collection rule to new collection with wql: $($wql)" -ForegroundColor Red
                }
        } else {
                Write-Host "Found collection"
        }
}

SCCM Module for PowerShell

In a post a few days ago I mentioned “some slightly modified functions from Michael Niehaus“.

Well… Why not share them.

Save this as a module, load it and play around.

2010-03-26 – Moved to https://snowland.se/sccm-posh/

Some examples on what you can do:

# List all available SCCM commands
Get-SCCMCommands

# Create an SCCM-Connection to the local server
$sccm = Connect-SCCMServer -Verbose

# Create a new collection with a collection rule
$newCollection = New-SCCMCollection -SccmServer $sccm -name "Some Collection Name" -Verbose
Add-SCCMCollectionRule -Server $sccm -collectionID $newRoot.CollectionId -queryExpression "SELECT * FROM SMS_R_System" -queryRuleName "All Systems" -Verbose

# Count files in the inboxes
$sccm | Get-SCCMInboxes

# Get a package
$MyPackage = Get-SCCMPackage -server $sccm -filter "Name = 'Some Package Name'"

If you have some comments, ideas and things to add… Comment this post or shoot me an .


Next Page »