Friday, October 3, 2008

Get-SmsWmi and Find-SmsID

I work with SMS (Systems Management Server) at work a lot, so I whipped up two functions that I've found invaluable for automating tasks in SMS, which I alluded to briefly in this post, which contains the source for the functions.

I thoght I'd post today about how these can be used and why they would be useful.  Think of it as the beginnings of a Get-SmsWmi cookbook.

Note:  I alias Get-SmsWmi to 'gsms', so keep that in mind for the examples.

Get a list of all available object nicknames.

C:\PSScripts> gsms

ERROR: You must enter a class name or nickname.

Valid nicknames are:

  AddRemovePrograms
  AdStatus
  Advertisement
  Collection
  ComputerSystem
  DistributionPoint
  LogicalDisk
  MembershipRule
  NetworkAdapter
  NetworkAdapterConfiguration
  OperatingSystem
  Package
  PackageStatus
  Program
  Query
  Server
  Service
  Site
  StatusMessage
  System
  WorkstationStatus
  User

Note: You only need to type as many characters as necessary to be unambiguous.


Get all computers with 'tojo' in the name.

gsms sys 'Name LIKE "%tojo%"' |
  Format-List ResourceId, 
  Name, 
  LastLogonUserName, 
  LastLogonUserDomain, 
  IPAddresses


ResourceId          : 220647
Name                : SOMETOJO-CORP
LastLogonUserName   : sometojo
LastLogonUserDomain : MYDOMAIN
IPAddresses         : {1.1.1.1}


Get all advertisements for the package with PackageID 'S01002BE'

gsms adv 'PackageId = "S00002BE"' |
  select AdvertisementID,
  AdvertisementName,
  @{Name = 'PackageName'; Expression = {(Find-SmsId -p 'S00002BE').Name}},
  PackageID,
  ProgramName |
    Format-List

AdvertisementID   : MV12071F
AdvertisementName : iPass Upgrade
PackageName       : iPass Upgrade
PackageID         : S00002BE
ProgramName       : Update iPass


Delete all packages for a particular distribution point.

gsms pack 'ServerNALPath LIKE "%servername%"' | % {$_.Delete()}


Get a directory listing of all Collections and their subcollections.

# Get-SmsCollectionTree
#
# Writes an indented list of collecitons by parent
#
# Args:
#   $root: The CollectionID of the parent collection
#   $indent: The indentation level for screen output

function Get-SmsCollectionTree {
  param([string]$root = 'COLLROOT',
        [int]$indent = 0)

  Get-SmsWmi SMS_CollectToSubCollect "parentCollectionID = '$root'" |
    % {$name =  (Find-SmsID -c $_.subCollectionID).Name
       Add-Member -InputObject $_ -Name 'sub_name' NoteProperty $name
       $_} |
      sort sub_name |
        % {Write-Host (('    ' * $indent) +
                        "+ $($_.sub_name) : $($_.subCollectionID)")
           Get-CollectionTree $_.subCollectionID ($indent + 1)}
}

Add all packages to a DP.

$dp = gsms SMS_SystemResourceList 'ServerName = "sms-dp-01" and RoleName = "SMS Distribution Point"'

foreach ($pkg in (gsms package)) {
  $new_object = ([wmiclass]"\\sms_srv\root\sms\site_MV1:SMS_DistributionPoint").CreateInstance()
  $new_object.ServerNALPath = $server.NALPath
  $new_object.PackageID = $pkg.PackageID
  $new_object.SiteCode = 'S00'
  $new_object.SiteName = 'My SMS Site'
  $new_object.Put()
}

Note:  That wmiclass/CreateInstance trick I did isn't very well documented, but if you need to create an instance of a class then that's how you can do it.  Also remember that when reading and writing to WMI for SMS, you should be connecting to the server with the SMS Provider, not necessarily the site server.  If you have a separate SQL Server then it might be that server.

That's it for today, but you can see how easy it is to start grabbing data directly from WMI and using it in a script.  If you have any questions drop them in the comments or shoot me an email, I don't get that much traffic.

For a complete list of the WMI classes, download the Configuration Manager 2007 SDK.

4 comments:

Martin Zugec said...

Hi,

I am also Powershell scripter for SCCM and currently I got stuck on one problem...

For my project, I need to create datatable where I want to store all IDs (PackageID, CollectionID...) together with type of object.

Do you know if there is simple WMI class (or anything that can be accessed using Powershell) where I could simply get list of all IDs' assigned by SCCM???

Thanks,
Martin

tojo2000 said...

Unfortunately, these are all stored in separate tables, so there really is no way to do that. Your best bet would be to pull each type. I would actually recommend pulling directly from each class. To speed it up, I'd try creating a hashtable of classes and ID names like this:

$classes = @{SMS_Package='PackageID';SMS_Advertisement='AdvertisementID'}

and then you can do a query like this:

foreach ($class in $classes.keys){
gwmi -namespace 'root/sms/site_S00' -computer 'providerserver' -query "Select $($classes.$class) from $class"}

So that you only get the property you're looking for and aren't downloading unnecessary information.

Martin Zugec said...

Well, that's what I am doing at this moment, I just hoped there is more elegant way :(

Function Global:SCCM\New-IDCache ([switch]$Force, [string]$ComputerName = $(Throw "-ComputerName argument is required"), [string]$SiteCode = $Null) {

[array]$Columns = $("Name", "Location", "ID", "ObjectType", "SiteCode")

Function AddRow ($Name, $Location, $ID, $ObjectType, $SiteCode) {
$NewRow = $SccmIdCache.NewRow()
$NewRow.Name = $Name
$NewRow.Location = $Location
$NewRow.ID = $ID
$NewRow.ObjectType = $ObjectType
$NewRow.SiteCode = $SiteCode
$SccmIdCache.Rows.Add($NewRow)
}

[hashtable]$Classes = @{
"SMS_Package" = @{"Name" = "Name"; "ID" = "PackageID"; "Type" = "SMS_Package"}
"SMS_Collection" = @{"Name" = "Name"; "ID" = "CollectionID"; "Type" = "SMS_Collection"}
"SMS_Advertisement" = @{"Name" = "AdvertisementName"; "ID" = "AdvertisementID"; "Type" = "SMS_Advertisement"}
}

# Create cache for SCCM IDs
If (-not $(Test-Path Variable:SccmIdCache) -or $Force) {
# Setup the Datatable Structure
$Global:SccmIdCache = New-Object system.Data.DataTable “SccmIdCache”
ForEach ($Column in $Columns) {$SccmIdCache.Columns.Add($(New-Object system.Data.DataColumn $Column,([string])))}
} Else {
$SccmIdCache.Rows.Clear()
}

If (-not $SiteCode) {$SiteCode = SCCM\Get-Site -ComputerName $ComputerName}

ForEach ($Class in $Classes.Values) {
ForEach ($Object in $(SCCM\Get-Object -ComputerName $ComputerName -SiteCode $SiteCode -ObjectType $Class.Type -Query "Select $($Class.ID), $($Class.Name) from $($Class.Type)")) {
AddRow -Name $Class.Name -ID $Class.ID -ObjectType $Class.Type -SiteCode $SiteCode
}
}
}

Martin Zugec said...

Hehe, this is how it looks like when you wrote code on the fly without actually trying it :)

AddRow syntax is of course little bit different:

AddRow -Name $Object.$($Class.Name) -ID $Object.$($Class.ID) -ObjectType $Class.Type -SiteCode $SiteCode