Checking Your Boundaries From IE

This article, that was published today on Microsoft’s Manageability blog, got me thinking:-

http://blogs.technet.com/b/manageabilityguys/archive/2011/06/30/testing-slp-availability.aspx

You may have seen in one of my previous articles, that you can create your own search providers in Internet Explorer, by going to http://www.ieaddons.com/createsearch.aspx (see my previous article for an explanation).

So, I thought I’d create a couple of quick searches:-

Sccm AD Site Boundary Checker - http://<my slp>/sms_slp/slp.dll?site&ad=TEST
Sccm Subnet Boundary Checker - http://<my slp>/sms_slp/slp.dll?site&ip=TEST

An example of running this is shown :-

ieBoundChk

Posted by Tom_Watson | with no comments
Filed under: ,

Software Metering Suites And Their Individual Applications

One feature of Software Metering is having the ability to meter multiple applications as one “suite” of applications.  The method Microsoft suggests it to give multiple rules the same name.  E.g. for Office 2007:-

Software Metering Rule Name File Name Original File Name Version
Microsoft Office 2007 MSACCESS.EXE MSACCESS.EXE 12.*
Microsoft Office 2007 EXCEL.EXE EXCEL.EXE 12.*
Microsoft Office 2007 INFOPATH.EXE INFOPATH.EXE 12.*
Microsoft Office 2007 OUTLOOK.EXE OUTLOOK.EXE 12.*
Microsoft Office 2007 POWERPNT.EXE POWERPNT.EXE 12.*
Microsoft Office 2007 MSPUB.EXE MSPUB.EXE 12.*
Microsoft Office 2007 WINWORD.EXE WINWORD.EXE 12.*

So basically what this does is if you run one of the Software Metering reports that looks for a rule name, and you select “Microsoft Office 2007”, it will match instances where any of those files have been ran.  Now that may be fine if you wanted to meter suites.  But what if someone wants a report on one of those individual applications (e.g. Outlook)?

The workaround is to change the rule names like the example below shows:-

Software Metering Rule Name File Name Original File Name Version
Microsoft Office Access 2007 MSACCESS.EXE MSACCESS.EXE 12.*
Microsoft Office Excel 2007 EXCEL.EXE EXCEL.EXE 12.*
Microsoft Office InfoPath 2007 INFOPATH.EXE INFOPATH.EXE 12.*
Microsoft Office Outlook 2007 OUTLOOK.EXE OUTLOOK.EXE 12.*
Microsoft Office PowerPoint 2007 POWERPNT.EXE POWERPNT.EXE 12.*
Microsoft Office Publisher 2007 MSPUB.EXE MSPUB.EXE 12.*
Microsoft Office Word 2007 WINWORD.EXE WINWORD.EXE 12.*

Now on face value, you may think this has removed the ability to run a report for a “suite”.  However, if you make a slight change to your metering reports, in the criteria sections (i.e. WHERE xxx…), you will have the ability to monitor a “suite” again, by picking a wildcard for your suite.

e.g. change the WHERE clause in the "Computers that have run a specific metered software program" :-

Replace

where mf.ProductName = @RuleName

with

where mf.ProductName LIKE @RuleName

As you can see this would change the report to allow wildcards.  So in our example, if you wanted to find systems that have ran anything from the Microsoft Office 2007 “suite” you would use the following criteria :-

Microsoft Office%2007

Replacing “=” with “LIKE” in the WHERE clause, lets you use wildcards.  “%” is the most common SQL wildcard, which simply means match anything.

The simple reason this works is that the clients have no idea of rule names.  They simply know rules by their ID, and criteria (filename and version).  The rule name is simply a human readable tag for the rule to be used when running reports.

Posted by Tom_Watson | with no comments
Filed under: , ,

System Warranty Report

I had this report posted on the myITforum forums before, but it’s been a little while since I updated it.  Until now.  Basically this takes the serial number from common desktop/laptop vendors, and provide URLs to allow you to lookup the warranty information on those vendors’ websites.

SELECT DISTINCT
       cs.Name0 AS "Computer Name",
       enc.SerialNumber0,
       enc.SMBIOSAssetTag0,
       bios.SerialNumber0 AS "PC Bios Serial Number",
       bios.ReleaseDate0 AS "PC Bios Release Date",
       cs.Manufacturer0,
       cs.Model0,
       "Estimated Date of CPU Manufacture" =
         CASE
           WHEN (CAST(cpu.CPU_Birth AS VARCHAR) IS NULL) THEN '(Not Available)'
           ELSE  CAST(cpu.CPU_Birth AS VARCHAR)
         END,
       pr.IsMulticore0 AS "Is Multi Processor System",
       CASE
         WHEN cs.Manufacturer0 LIKE 'Hewlett%' THEN '
http://h20000.www2.hp.com/bizsupport/TechSupport/WarrantyResults.jsp?sn=' + enc.serialnumber0 + '&country=US'
         WHEN cs.Manufacturer0 LIKE 'HP%' THEN '
http://h20000.www2.hp.com/bizsupport/TechSupport/WarrantyResults.jsp?sn=' + enc.SerialNumber0 + '&country=US'
         WHEN cs.Manufacturer0 LIKE 'Compaq%' THEN '
http://h20000.www2.hp.com/bizsupport/TechSupport/WarrantyResults.jsp?sn=' + enc.SerialNumber0 + '&country=US'
         WHEN cs.Manufacturer0 LIKE 'Dell%' THEN '
http://support.dell.com/support/topics/global.aspx/support/my_systems_info/details?servicetag=' + enc.SerialNumber0
         WHEN cs.Manufacturer0 LIKE 'IBM%' THEN '
http://www-307.ibm.com/pc/support/site.wss/warrantyLookup.do?type=' + LEFT (cs.model0, 4) + '&serial=' + bios.SerialNumber0 + '&country=897'
         WHEN cs.Manufacturer0 LIKE 'Lenovo%' THEN '
http://www-307.ibm.com/pc/support/site.wss/warrantyLookup.do?type=' + LEFT (cs.model0, 4) + '&serial=' + bios.SerialNumber0 + '&country=897'
         WHEN cs.Manufacturer0 LIKE 'Acer%' THEN '
http://secure3.tx.acer.com/FindSystem/findsystem.aspx?Title=Information&sn=' + bios.SerialNumber0
         ELSE '(Not available)'
       END AS "Warranty Information"
FROM   dbo.v_GS_PROCESSOR pr
JOIN   dbo.v_GS_SYSTEM_ENCLOSURE enc
  ON   enc.ResourceID = pr.ResourceID
JOIN   dbo.v_GS_PC_BIOS bios
  ON   bios.ResourceID = pr.ResourceID
JOIN   dbo.v_GS_COMPUTER_SYSTEM cs
  ON   cs.ResourceID = pr.ResourceID
LEFT JOIN dbo.v_LU_CPU cpu
  ON   Lower(cpu.CPUHash) = Lower(pr.CPUHash0)
WHERE  cs.Name0 = @Name
  AND  enc.ChassisTypes0 <> 12

As you can see, it lists warranty URLs for HP/Compaq, Dell, IBM/Lenovo, and Acer.  I couldn’t find URLs for other vendors, but if anyone does know of any of these, let me know.

Posted by Tom_Watson | with no comments
Filed under: , ,

Creating Standard Sender Addresses VBS

For the most part I was creating this for myself, as I had a requirement to create a lot of these for a new server.  I knew about the tool CrAddr.exe which existed in the SMS 2003 Support Tools, but it seems that this doesn’t quite work 100% in ConfigMgr 2007.  It seems to create the address, but gives it a corrupt site address account.

Anyway, after a bit of searching, I found a script at http://technet.microsoft.com/en-us/library/cc180643.aspx.  However, I noticed that it was hard coded with the site code, and server values.  So to make it a bit more flexible, I changed this to :-

'''''''''''''''''''''''''''''''''''
' Modified from
http://technet.microsoft.com/en-us/library/cc180643.aspx
' allowing command line parameters

Dim iParameterCount
iParameterCount = WScript.Arguments.Count
If iParameterCount < 4 Then
    WScript.Echo ""
    WScript.Echo "CreateAddress.vbs v1.0"
    WScript.Echo " "
    WScript.Echo "Creates Standard Sender Address (Full schedule/Unlimited rate)"
    WScript.Echo " "
    WScript.Echo "Usage:-"
    WScript.Echo "cscript.exe CreateAddress.vbs <LocalSiteCode> <LocalSiteServer> <DestSiteCode> <DestSiteServer>"
    WScript.Echo " "
    WScript.Echo "Note, <LocalSiteCode> is the site code for the server that this is being created on."
    WScript.Echo " "
    WScript.Echo "Example: cscript.exe CreateAddress.vbs ABC ABCSCCM01 DEF DEFSCCM01"
    WScript.Echo ""
    WScript.Quit (0)
End If

strSiteCode=WScript.Arguments(0)
strSiteServer=WScript.Arguments(1)
Set lLocator = CreateObject("WbemScripting.SWbemLocator")
Set wbemservices = lLocator.ConnectServer(strSiteServer, "root\sms\site_" & strSiteCode)
set WbemContext=CreateObject("WbemScripting.SWbemNamedValueSet")
WbemContext.Add "SessionHandle",WbemServices.ExecMethod("SMS_SiteControlFile","GetSessionHandle").SessionHandle
WbemServices.ExecMethod "SMS_SiteControlFile.Filetype=2,Sitecode=""" & strSiteCode & """", "Refresh", , , WbemContext
'Spawn an SMS_SCI_Address instance
Set WbemInst = WbemServices.Get("SMS_SCI_Address").SpawnInstance_()
wbeminst.AddressType = "MS_LAN"
wbeminst.DesSiteCode = WScript.Arguments(2)
wbeminst.ItemName = WScript.Arguments(2) & "|MS_LAN"
wbeminst.ItemType = "Address"
wbeminst.Order = 1
wbeminst.SiteCode = strSitecode
wbeminst.UnlimitedRateForAll = 1
Set newep1 = WbemServices.Get("SMS_EmbeddedProperty").SpawnInstance_()
newep1.PropertyName = "Connection Point"
newep1.Value = 0
newep1.Value1 = WScript.Arguments(3)
newep1.Value2 = "SMS_SITE"
Set newep2 = WbemServices.Get("SMS_EmbeddedProperty").SpawnInstance_()
newep2.PropertyName = "LAN Login"
newep2.Value = 0
newep2.Value1 = ""  ' assuming no site address account
newep2.Value2 = ""  ' assuming no site address account
wbeminst.Props = Array(newep1, newep2)
WbemInst.Put_ , WbemContext
WbemServices.ExecMethod "SMS_SiteControlFile.Filetype=1,Sitecode=""" & strSitecode & """", "Commit", , , WbemContext
WbemServices.Get("SMS_SiteControlFile").ReleaseSessionHandle WbemContext.Item("SessionHandle").Value

'''''''''''''''''''''''''''''''''''

Note that this will create Standard Sender Addresses, with no Site Address Account, a Full Schedule, and Unlimited Rate Limit.  Also, there’s no major error checking, apart from counting the number of command line parameters.

Posted by Tom_Watson | with no comments
Filed under: ,

Watch Event Viewer In Real Time With MyEventViewer

MyEventViewer is available from http://www.nirsoft.net/utils/my_event_viewer.html

Watching events in realtime is new feature that was added to v1.40.  You can specify the polling period for new events in 1, 3, 5, or 10 seconds.  Almost like a Trace32.exe for events.

Posted by Tom_Watson | with no comments
Filed under:

NirCmd Now Has Balloon Popup Feature

I've posted previously about using AutoIt to create balloon popups, e.g. for reboot notifications.  NirCmd now provides an alternative way to do a balloon popup, in a simple tool that could be used in any scripting language of your choice.

Download NirCmd from http://www.nirsoft.net/utils/nircmd.html.  You can see the syntax for doing popup balloons at http://nircmd.nirsoft.net/trayballoon.html.

Warning – these websites may be blocked by many companies, as NirSoft also contains many password related utilities.

Posted by Tom_Watson | with no comments
Filed under: ,

lastLogonTimestamp in Active Directory User Discovery

This is something you might find a use for if you have Active Directory User Discovery enabled.  Active Directory User Discovery might be something you have enabled on your central site, but not your child sites, purely for reporting purposes.

One word of warning, before I delve into this.  lastLogonTimestamp may not actually display the last logon for a user, as its purpose is more to find inactive accounts, as opposed to the exact time a particular user last logged in:-

http://blogs.technet.com/askds/archive/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works.aspx

Remember the purpose of the lastLogontimeStamp attribute is locate inactive accounts not provide real-time logon information.

So anyway, this is still useful if that’s the type of information you are looking for.  To configure this on your ConfigMgr site is simply a case of adding an attribute to your discovery like so:-

lastLogonTimestamp

Once Active Directory User Discovery runs, it will populate with a lastLogonTimestamp column.

The second part of this is that the information is actually stored in Interger8 format.  This basically means it is stored as the number of 100-nanosecond intervals since 12:00 AM January 1, 1601.  For example “129085607775156538” basically means 12908560777515653800 nano-seconds since 12:00 AM January 1, 1601.  One way to convert is to use w32tm.exe /ntte.:-

w32tm /ntte <NT time epoch>
  Convert a NT system time, in (10^-7)s intervals from 0h 1-Jan 1601, into a readable format.

C:\ w32tm.exe /ntte 129085607775156538
149404 15:19:37.5156538 - 21/01/2010 15:19:37 (local time)

To do this in T-SQL, I borrowed a bit of code from http://myitforum.com/cs2/blogs/jnelson/archive/2009/08/25/140938.aspx and came up with the following report:-

SELECT User_Name0,
       Name0,
       DATEADD(mi,(lastLogontimeStamp0  / 600000000) - 157258080,0) AS [Last Logon Time Stamp (UTC)]
FROM   dbo.v_R_User
WHERE  User_Name0 LIKE @variable

This gives you an output like so:-

User Name Name Last Logon Time Stamp (UTC)
DoeJ ACME\DoeJ (John Doe) 24/01/2010 22:30:00
Posted by Tom_Watson | with no comments

KB974014 - Windows 7 and 2008 R2 support added for SMS 2003

http://support.microsoft.com/kb/974014

One thing to note is that it says this will be the last update of its kind for an OS support in SMS 2003.

Posted by Tom_Watson | with no comments
Filed under:

Determine Overlapping Active Directory Sites

This is merely pointing out an excellent script I found when trying to troubleshoot SMS/SCCM site boundary issues.  One issue I had was where different active directory sites had overlapping subnets.  Anyway, this script came from http://web2.minasi.com/forum/topic.asp?TOPIC_ID=13286 and requires Perl to run. :-

#!perl -w
# wkasdo 2004
# find overlapping subnets in AD. Those are not necessarily a
# bad thing if it was done on purpose, but can be the cause of
# some very confusing logon behaviour.

use strict;
use Win32::OLE 'in';
$Win32::OLE::Warn = 3;

my @subnets;        # array of anonymous hashes {cn, from, to}
my %overlaps;       # hash {cn} of anonymous arrays of subnets
my ($subnet, $from, $to, $i, $j);

# read all subnets from AD, convert to integer range, and sort.
foreach $subnet (&read_all_subnets) {
    ($from, $to) = &subnet2range ($subnet);
    push @subnets, { cn => $subnet, from => $from, to => $to };
}
@subnets = sort { $a->{from} <=> $b->{from} } @subnets;

# compare all possible subnets for overlap; depend on sort order.
for ($i=0; $i<=$#subnets; $i++) {
    for ($j=$i+1; $j<=$#subnets; $j++) {
        last if $subnets[$i]->{to} < $subnets[$j]->{from};    # no possible overlap anymore;
        push @{$overlaps{$subnets[$i]->{cn}}}, $subnets[$j]->{cn} if $subnets[$i]->{to} >= $subnets[$j]->{from};
    }
}

if (scalar (keys %overlaps)) {
    print "The following overlapping subnets were found:\n";
    foreach $subnet (sort keys %overlaps) {
        printf "%-20s : %s\n", $subnet, join (", ", sort @{$overlaps{$subnet}});
    }
} else {
    print "No overlapping subnets found.\n";
}
exit (0);
# &read_all_subnets
# read the subnets from the configuration context and return as array.
#
sub read_all_subnets {
    my ($cnc, $o_subnets, $o_subnet, @subnets);

    $cnc = Win32::OLE->GetObject("LDAP://RootDSE")->Get("configurationNamingContext");
    $o_subnets = Win32::OLE->GetObject("LDAP://CN=Subnets,CN=Sites,$cnc");
    $o_subnets->{Filter} = ["subnet"];
    foreach $o_subnet (in $o_subnets) {
       push @subnets, $o_subnet->Get("cn");
    }
    return @subnets;
}

# &subnet2range ($subnet)
# convert subnets to integers in order to compare them later.
# A subnet looks like this: 10.1.2.0/24
# returns beginning and end of subnet as integer
#
sub subnet2range {
    my $subnet = shift (@_);
    my ($from, $to);
    $subnet =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)\/(\d+)$/ || die "bad subnet $subnet\n";
    $from = $1*2**24 + $2*2**16 + $3*2**8 + $4;
    $to = $from + 2**(32-$5) - 1;
    return ($from, $to);
}

I am by no means a Perl expert, but this indeed did show some overlaps from different sites that needed resolving.  A copy of Perl, which is free to use, can be downloaded from http://www.activestate.com/activeperl/.

A simple VBScript to list all your active directory site names and their subnets is:-

On Error Resume Next

Set objRootDSE = GetObject("LDAP://RootDSE")
strConfigurationNC = objRootDSE.Get("configurationNamingContext")
strSubnetsContainer = "LDAP://cn=Subnets,cn=Sites," & strConfigurationNC
Set objSubnetsContainer = GetObject(strSubnetsContainer)
objSubnetsContainer.Filter = Array("subnet")
Set objHash = CreateObject("Scripting.Dictionary")
For Each objSubnet In objSubnetsContainer
    objSubnet.GetInfoEx Array("siteObject"), 0
    strSiteObjectDN = objSubnet.Get("siteObject")
    strSiteObjectName = Split(Split(strSiteObjectDN, ",")(0), "=")(1)
    If objHash.Exists(strSiteObjectName) Then
        objHash(strSiteObjectName) = objHash(strSiteObjectName) & "," & _
            Split(objSubnet.Name, "=")(1)
    Else
        objHash.Add strSiteObjectName, Split(objSubnet.Name, "=")(1)
    End If
Next
For Each strKey In objHash.Keys
    WScript.Echo strKey & "," & objHash(strKey)
Next

Credit http://www.activxperts.com/activmonitor/windowsmanagement/scripts/activedirectory/sites/#LSADS.htm

Posted by Tom_Watson | with no comments

Finding DNS Forwarders Through ConfigMgr

There are a couple of ways of doing this, though I will describe the first in more detail.  This might be useful if you need information to help redesign your DNS infrastructure.  Anyway, DNS forwarders configured on your DNS servers can be obtained in a couple of ways.

  1. Registry – HKLM\SYSTEM\CurrentControlSet\Services\Dns\Parameters|Forwarders (see http://support.microsoft.com/kb/813963 for details)
  2. WMI – wmic.exe /NAMESPACE:\\root\MicrosoftDNS PATH MicrosoftDNS_Server GET Forwarders (see http://msdn.microsoft.com/en-us/library/ms682725(VS.85).aspx for details)

To use the registry method, you could use the technique explained in http://myitforum.com/cs2/blogs/tom_watson/archive/2009/09/16/alternative-registry-inventory-with-configmgr.aspx by adding the additional entry to configuration.mof

[DYNPROPS]
instance of Registry_Values
{
                 KeyName="HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Dns\\Parameters|Forwarders";
  [PropertyContext("local|HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Dns\\Parameters|Forwarders")
  ,Dynamic
  ,Provider("RegPropProv")] String;
};

Posted by Tom_Watson | with no comments

Reboot Nag, Using AutoIt

I thought I’d start 2010 with a blog posting pretty early on.  I’ve had a blog for a few months, and only blogged a few times.  I’m hoping this year I can get a few more done.  Anyway, onto the blog…

Something I was trying to get my head around lately was how to do a reboot nag that displays a balloon, similar to Automatic Updates.  This is something that could be fairly useful at the tail end of SMS packages etc.  My first option of VBScript, as far as I know, doesn’t do notifications using balloons.  I searched for other ways, and couldn't come up with anything, apart from a few “freeware” that looked a bit suspicious.  Then I remembered AutoIT v3 - http://www.autoitscript.com/autoit3 which I’d used for some other scripts in the past.  AutoIt, for those who are unfamiliar, is a scripting language that is more catered for handling things on the screen (moving windows, selecting items, typing characters, handling input etc.).  Anyway, here’s the script I came up with:-

::::::::::::::
Nag.au3
::::::::::::::

;
; AutoIt Version: 3.0
; Language:       English
; Platform:       Win9x/NT
; Author:         Tom Watson
;
; Script Function:
;   Displays a balloon message to restart the system
;

TraySetIcon("mucltui.dll",3)
TraySetToolTip ("Please restart your system")
Break(0) ;Disable break

While 1 = 1
   TrayTip("Global IT", "Critical updates have been installed on your system. Please save your work and restart your system as soon as possible.", 10, 1)
   Sleep (55000)
WEnd

(Watch the word wrap).  All this does is display a balloon for 5 seconds, and waits for another 55 seconds before redisplaying it in an infinite WHILE loop.  It uses the Automatic Updates DLL for its icon, and if you hover over the icon while the balloon isn’t displayed, it displays a much shorter summary.  You could modify this to suit your needs.

Nag Balloon

AutoIt has lots of functions you can use to display notifications, handle input etc. (see http://www.autoitscript.com/autoit3/docs/functions.htm for more details).  Also, one of the things you can do with AutoIt is to compile your scripts into a .EXE, which is what you would normally do if using your script on other PCs.  If you compiled the script above to an .EXE, you could launch it in a number of ways.  e.g. from a .VBS script:-

::::::::::::::
Nag.vbs
::::::::::::::

Set objShell = CreateObject("Wscript.Shell")

strPath = Wscript.ScriptFullName

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.GetFile(strPath)

strFolder = objFSO.GetParentFolderName(objFile)

strPath =  CHR(34) & strFolder & "\Nag.exe" & CHR(34)
objShell.Run strPath, 1, FALSE

Or even launch it remotely by using PSExec:-

D:\My AutoIt3 Scripts> psexec.exe -s -c -f -d -i \\pc65 Nag.exe

PsExec v1.97 - Execute processes remotely
Copyright (C) 2001-2009 Mark Russinovich
Sysinternals - www.sysinternals.com

Nag.exe started on pc65 with process ID 4644.


I hope this gives you some ideas for an alternative way to handle reboots, or even do other more complex tasks.

Posted by Tom_Watson | 2 comment(s)
Filed under:

Top 10 Screen Resolutions

I won’t get too technical in this post, as this was more purely an exercise in curiosity.  I was looking at stats for web browsers one day, and found the following website.

http://w3counter.com/globalstats.php

One thing that caught my eye was the stats on screen resolutions.  I thought to myself that this would be interesting to pull from SMS / SCCM.  So I took a look at some of the information that is gathered through hardware inventory.  I noticed in the v_GS_VIDEO_CONTROLLER section, horizontal and vertical resolution.  Now, if I combined these two values, and did a count, I could get similar stats.  Anyway, without going too much into the SQL statement’s syntax, here’s the code I used.

SELECT TOP 10 CAST (CurrentHorizontalResolution0 AS varchar) + ' x ' +
    CAST (CurrentVerticalResolution0  AS varchar) AS 'Screen Resolution',
    COUNT(*) AS 'Count'
FROM    dbo.v_GS_VIDEO_CONTROLLER
WHERE    VideoModeDescription0 IS NOT NULL
GROUP BY CAST (CurrentHorizontalResolution0 AS varchar) + ' x ' + CAST (CurrentVerticalResolution0  AS varchar)
ORDER BY Count DESC

I had to CAST the values to varchar types, so that I could put ‘ x ‘ in the middle.  Here’s an example displayed as a pie chart.

 

ScreenPie

“1024 x 768” was still tops, closely followed by “1280 x 1024”.

Posted by Tom_Watson | with no comments
Filed under: ,

Simplified Asset Intelligence Licence 01C Report

Now this is more an exercise in common sense by removing redundant data from a report, rather than trying to do the impossible.  The scenario I met was I had to use this report, but run it for all systems in our organisation, for a Microsoft application that has a very large install base.  So off I went to run the report.  I picked the “SMS00001 - All Systems” collection, picked the application, and the sales channel and clicked Run.  Result:-

This error (HTTP 500 Internal Server Error) means that the website you are visiting had a server problem which prevented the webpage from displaying.

Now, I knew I hadn’t reached the 10000 row limit, though it did take about 90 seconds, so it was probably timing out, or reaching the ASP buffer limit.  Now I knew I can increase limits in IIS etc. but I thought I’d take a look at what the report produced for something with a much smaller install base.  Without divulging the actual data, I saw that it gave me the following columns:-

Computer Name, SMS Site, Top Console User, Serial Number, Asset Tag, Computer Manufacturer, Computer Model, Product ID, DSL ID, Installation Type,

Now since this was a licensing report, the people needing this didn’t need a lot of these columns.  Also, they are probably coming from different tables being joined to get this.  Looking at the code for the SELECT part of the SQL statement, I saw:-

SELECT distinct v_R_System_Valid.Netbios_Name0 AS [Computer Name],
v_Site.SiteName as [SMS Site],
v_GS_SYSTEM_CONSOLE_USAGE_MAXGROUP.TopConsoleUser0 AS [Top Console User],
v_GS_SYSTEM_ENCLOSURE_UNIQUE.SerialNumber0 AS [Serial Number],
v_GS_SYSTEM_ENCLOSURE_UNIQUE.SMBIOSAssetTag0 AS [Asset Tag],
v_GS_COMPUTER_SYSTEM.Manufacturer0 AS [Computer Manufacturer],
v_GS_COMPUTER_SYSTEM.Model0 AS [Computer Model],
v_GS_INSTALLED_SOFTWARE.ProductID0 AS [Product ID],
v_GS_INSTALLED_SOFTWARE.CM_DSLID0 as [DSL ID],
v_GS_INSTALLED_SOFTWARE.InstallType0 as [Installation Type]

There's also a prompt for collection.  Querying on all those different views, through multiple JOINs, is usually a sure fire way to make the report harder to run.  In total 9 views were being JOINed.  So what about trimming this down to what is actually relevant in the report?  Here’s what I removed.:-

  1. The team who were running this, wanted it for “All Systems”.  Now I could have kept the prompt in for collection, and let them choose the “All Systems” collection.  However, doing so requires an extra JOIN.  Removing that prompt, allowed me to remove the JOIN to v_FullCollectionMembership.  That took it down to 8 SQL views being JOINed.
  2. “Serial Number” and “Asset Tag” shouldn’t really be required in a licensing report.  So removed those columns from the report, and the JOIN to v_GS_SYSTEM_ENCLOSURE_UNIQUE.  That took it to 7 SQL views.
  3. “Top Console User” is a bit of a luxury, and requires another view.  Replaced with “User_Name0” from the v_R_System_Valid view.  So no longer needed the v_GS_SYSTEM_CONSOLE_USAGE_MAXGROUP view, which takes it down to 6 SQL views.
  4. The descriptive Site Name is a little bit of a luxury, and requires JOINing v_Site.SiteCode to v_FullCollectionMembership.SiteCode.  I already removed the v_FullCollectionMembership view, and used v_RA_System_SMSInstalledSites instead.  It only gives the Site Code, but the saving in report complexity outweighed having the site description.  Since we replaced the v_Site view with v_RA_System_SMSInstalledSites, it was still at 6 SQL views.
  5. Lastly, Manufacturer and Model again wasn’t really relevant for a licensing report.  So removed those columns, and dropped the v_GS_COMPUTER_SYSTEM view, taking it down to 5 SQL views being JOINed.

So with all that, I ran the report again with the same criteria, which this time only took about 5 seconds, and gave me the following columns.:-

Netbios Name, Installed Site Code, User Name, Product ID, DSL ID,

A big saving by simply removing a lot of redundant data from the report, which took the number of SQL views being JOINed from 9 down to 5.  And for those who want to see my final code, it’s:-

if    @Channel = ''
    OR @Channel = 'Not Available'
Begin
    Select @Channel = NULL
End

select  distinct Sys.Netbios_Name0,
    iSite.SMS_Installed_Sites0,
    Sys.User_Name0,
    Sw.ProductID0 AS [Product ID],
    Sw.CM_DSLID0 as [DSL ID]
from    dbo.v_GS_INSTALLED_SOFTWARE_MS Swms
join    dbo.v_GS_INSTALLED_SOFTWARE Sw
  on    (Swms.SoftwareCode0 = Sw.SoftwareCode0
and     Swms.ResourceID = Sw.ResourceID)
join    dbo.v_R_System_Valid Sys
  on    Sys.ResourceID = Swms.ResourceID
join    dbo.v_RA_System_SMSInstalledSites iSite
  on    Sys.ResourceID = iSite.ResourceID
join    dbo.v_LU_MSProd MsCat
  on    MsCat.MPC = Swms.MPC0
where    MsCat.MLSProductName = @ProductName
  and    Swms.ChannelCode0 = @Channel
Order by Sys.Netbios_Name0 ASC

Posted by Tom_Watson | with no comments
Filed under: ,

Management Point as IE Search

This is probably more useful if you have a large multi site environment.  As you probably know, if you manage SMS 2003 or ConfigMgr, you can quickly find the status of a Management Point by connecting to:-

http://<Management_Point>/sms_mp/.sms_aut?mpcert

When you open this, it will display a huge hexadecimal number, which usually indicates your Management Point is functioning.  See http://technet.microsoft.com/en-us/library/bb932118.aspx

Now I usually can’t remember that address.  But you can easily add a Search Provider in Internet Explorer 7 or 8.

Find More Providers

Clicking on “Find More Providers” takes you to a Microsoft page that lets you pick from a list of search providers, or “Create Your Own”.  Alternatively, go to http://www.ieaddons.com/createsearch.aspx .

To create your own, enter the address:-

http://TEST/sms_mp/.sms_aut?mpcert

“TEST” is effectively the variable for the search provider you are creating.  Then give it a name of your choice – e.g. “Management Point Status”.  Finally, click “Install”.

Now all you have to do is enter your server name in the Search box, and select “Management Point Status”.

Management Point Status

You can use this technique for other items that you regularly use in reports etc., e.g.

Computer Details
http://<your server>/SMSReporting_XXX/MachDetails.asp?Machine=TEST
McAfee ePO Agent Status
http://TEST:8081 (assuming you use 8081 as your client port)

I am sure you can think of other ideas for this.

Posted by Tom_Watson | 2 comment(s)
Filed under: ,

Alternative Registry Inventory with ConfigMgr

This is based on an idea I had to help improve the registry inventory capabilities of Hardware Inventory.  The idea was inspired by Mark Cochrane’s RegkeyToMof tool, as well as from some advice from Sherry Kissinger.  There was also a thread on the mssms list where people suggested improvements for ConfigMgr, and I suggested Registry Inventory.

The concept is to put multiple ad hoc registry values into the Hardware Inventory.  The common way of doing this was to add multiple values into a single instance. In Resource Explorer this displays as a single row with multiple columns.  I found this a bit too unwieldy after a while.

Resource Explorer Take 1

My idea was to try to effectively pivot this data round so that you had multiple rows, with minimal columns.

Resource Explorer Take 2

I simply felt scrolling vertically was nicer than scrolling horizontally. I also like the idea of having the full registry path in the Resource Explorer view.

Here is how I achieved this.

Addition to SMS_DEF.MOF (Note. This part never needs to change)

//`'`*._.*`'`*-
// Registry_Values Reporting Class

//`'`*._.*`'`*-

#pragma namespace("\\\\.\\ROOT\\CIMV2\\SMS")

#pragma deleteclass("Registry_Values",NOFAIL)

[ SMS_Report (TRUE),

  SMS_Group_Name ("Registry_Values"),

  SMS_Class_ID ("CUSTOM|Registry_Values|1.0")
]


Class Registry_Values : SMS_Class_Template
{
  [SMS_Report(TRUE),key] string KeyName; // The registry value name
  [SMS_Report(TRUE)] string String;      // For REG_SZ or REG_EXPAND_SZ values
  [SMS_Report(TRUE)] string Multi[];     // For REG_MULTI_SZ values
  [SMS_Report(TRUE)] Uint64 Integer;     // For REG_DWORD values
};

Addition to CONFIGURATION.MOF

//`'`*._.*`'`*-
// Registry_Values Data Class
//`'`*._.*`'`*-

#pragma namespace("\\\\.\\ROOT\\CIMV2")
#pragma deleteclass("Registry_Values",NOFAIL)
[DYNPROPS]
Class Registry_Values
{
  [key] string KeyName=""; // The registry value name
  String String;           // For REG_SZ or REG_EXPAND_SZ values
  String Multi[];          // For REG_MULTI_SZ values
  Uint64 Integer;          // For REG_DWORD values
};

[DYNPROPS]
instance of Registry_Values
{
                 KeyName="HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon|AltDefaultUserName";
  [PropertyContext("Local|HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon|AltDefaultUserName")
  ,Dynamic,Provider("RegPropProv")] String;
};

[DYNPROPS]
instance of Registry_Values
{
                 KeyName="HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon|DefaultUserName";
  [PropertyContext("Local|HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon|DefaultUserName")
  ,Dynamic,Provider("RegPropProv")] String;
};

[DYNPROPS]
instance of Registry_Values
{
                 KeyName="HKEY_LOCAL_MACHINE\\Software\\Policies\\Microsoft\\Windows\\Installer|Logging";
  [PropertyContext("Local|HKEY_LOCAL_MACHINE\\Software\\Policies\\Microsoft\\Windows\\Installer|Logging")
  ,Dynamic,Provider("RegPropProv")] String;
};

[DYNPROPS]
instance of Registry_Values
{
                 KeyName="HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\SMS\\Client\\Client Components\\Remote Control|Permission Required";
  [PropertyContext("Local|HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\SMS\\Client\\Client Components\\Remote Control|Permission Required")
  ,Dynamic,Provider("RegPropProv")] Integer;
};

[DYNPROPS]
instance of Registry_Values
{
                 KeyName="HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\SMS\\Client\\Client Components\\Remote Control|PermittedViewers";
  [PropertyContext("Local|HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\SMS\\Client\\Client Components\\Remote Control|PermittedViewers")
  ,Dynamic,Provider("RegPropProv")] Multi;
};

[DYNPROPS]
instance of Registry_Values
{
                 KeyName="HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\VBSFile\\Shell\\Open\\Command|";
  [PropertyContext("Local|HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\VBSFile\\Shell\\Open\\Command|")
  ,Dynamic,Provider("RegPropProv")] String;
};

-----------------------

If you look at the addition to SMS_DEF.MOF, this never needs to change.

However, to report multiple values is simply a matter of adding an extra “[DYNPROPS] instance of Registry_Values...” section into CONFIGURATION.MOF. Let’s take a look at one section to get an idea of the syntax.

-----------------------

[DYNPROPS]
instance of Registry_Values
{
                 KeyName="HKEY_LOCAL_MACHINE\\Software\\Policies\\Microsoft\\Windows\\Installer|Logging";
  [PropertyContext("Local|HKEY_LOCAL_MACHINE\\Software\\Policies\\Microsoft\\Windows\\Installer|Logging")
  ,Dynamic,Provider("RegPropProv")] String;
};

-----------------------

I’ve indented the “KeyName=...” line, so you can compare it with the “[PropertyContext...” line. You should be able to see that they are the same from HKEY_LOC... through to Logging".

The backslash is a special character, hence to reference a backslash, you need to use “\\”. The text after “|” is the value’s name.  For (Default) values, there is no text after the “|”, as the last example shows ( i.e. KeyName="HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\VBSFile\\Shell\\Open\\Command|"; )

On the last line before the “};”, there are three possibilities, depending on the type of value being inventoried.:-

-----------------------

,Dynamic,Provider("RegPropProv")] String;   // For REG_SZ or REG_EXPAND_SZ values

    or

,Dynamic,Provider("RegPropProv")] Multi;    // For REG_MULTI_SZ values

    or

,Dynamic,Provider("RegPropProv")] Integer;  // For REG_DWORD values

-----------------------

I hope this is fairly self explanatory, and is useful for creating your own ad hoc registry inventorying.

Posted by Tom_Watson | with no comments
Filed under: ,