4/06/2011

Switch from Local System to User context

Sometimes there’s a need to switch from Local System context to the current logged on User.
The following article describes how to achieve this.

In short words the following happens:
1. Something is executed in local system context
2. At the end a EventLog entry is written
3. A Scheduled Tasks gets trigged by the created EventLog entry
4. The Scheduled Tasks which is running in User context executes another script

One example is e.g. to inform Users about an Installation which was executed from the Local System Account. The Application Deployment process executes the CreateEventToInform.vbs Script at the end of the installation to create the EventLog entry for informing the User

CreateEventToInform.vbs
Dim objShell : Set objShell = CreateObject("WScript.Shell")
Dim strMessage : strMessage = "Dear User this is a message from your local system"
objShell.LogEvent(4, strMessage)


InformUser.vbs
Dim strMessage
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & "."
& "\root\cimv2")
Set colLoggedEvents = objWMIService.ExecQuery("Select * from Win32_NTLogEvent Where Logfile = 'Application' and SourceName='WSH' and EventCode=4 and Type='Information'")

For Each objEvent In colLoggedEvents
strMessage = objEvent.Message
Exit For
Next

MsgBox(strMessage, vbInformation + vbOKOnly, "Your Company")


It could also be used as an immediately executed “ActiveSetup” for the current logged User. The Application Deployment process executes the CreateEventToExecute.vbs Script at the end of the installation to create the EventLog entry for performing a Active Setup for the current User

CreateEventToExecute.vbs
Dim objShell : Set objShell = CreateObject("WScript.Shell")
Dim strCommandLine : strCommandLine = "msiexex /fup "
objShell.LogEvent(4, strCommandLine)


ExecuteUserPart.vbs
Dim objShell : Set objShell = CreateObject("Wscript.Shell")
Dim strCommandLine
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & "." & "\root\cimv2")
Set colLoggedEvents = objWMIService.ExecQuery("Select * from Win32_NTLogEvent Where Logfile = 'Application' and SourceName='WSH' and EventCode=4 and Type='Information'")

For Each objEvent In colLoggedEvents
strCommandLine = objEvent.Message
Exit For
Next

objShell.Run(strCommandLine, 1, False)


To make the whole think work we have to create a trigger which reacts to the EventLog Entry which was created. Create a Scheduled Tasks which is executed in the User context as listed below for this

Note: You can deploy scheduled tasks to many computer automatically e.g. with Group Policies

Now as soon as the EventID is written the Scheduled Tasks executes the below listed command



Further thoughts:
You could create a whole Application which is written variable so that you can perform different actions in User context. To achieve this easily you could tag the EventLog Message with specific keywords. Later the Application parses the EventLog Message and you can react on this. So you can use it for different scenarios. Example:

WriteEventLog.vbs
Dim objShell : Set objShell = CreateObject("WScript.Shell")
Dim strMessage : strMessage = "[Inform]Dear User now there's happening something" &vbNewLine &"[Execute]msiexex /fup "
objShell.LogEvent 4, strMessage


PerformInUserContext.vbs
Dim objShell : objShell = CreateObject("Wscript.Shell")
Dim strMessage, arrEventEntry
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & "." & "\root\cimv2")
Set colLoggedEvents = objWMIService.ExecQuery("Select * from Win32_NTLogEvent Where Logfile = 'Application' and SourceName='WSH' and EventCode=4 and Type='Information'")

For Each objEvent In colLoggedEvents
strMessage = objEvent.Message
Exit For
Next

arrEventEntry = Split(strMessage, vbNewLine)

For Each strLine In arrEventEntry
If InStr(strLine, "[Inform]") Then
MsgBox(Replace(strLine, "[Inform]", ""))
ElseIf InStr(strLine, "[Execute]") Then
objShell.Run(Replace(strLine, "[Execute]", ""), 1, False)
End If
Next

2/16/2011

Windows 7 SP1 Released

Windows 7 and Windows Server 2008 R2 Service Pack 1 is no available for download on MSDN

2/01/2011

vbScript - Get running environment

The following vbScript Function delivers the value true if the script runs within a task sequence environment. If not the functions delivers the value false. This function can be useful in scripts like wrapper and so on.


...
If RunningInTaskSequence = True Then
...

Function RunningInTaskSequence()
On Error Resume Next
Err.Clear
Dim objTSEnv : Set objTSEnv = CreateObject("Microsoft.SMS.TSEnvironment")
If Err Then
'Script does not run within the Task Sequence environment
RunningInTaskSequence = False
Else
'Script does run within the Task Sequence environment
RunningInTaskSequence = True
End If
On Error GoTo 0
End Function

Thanks to Mark Cochrane and Sherry Kissinger...

...for mention me in the new release of the RegKeyToMof v2.5 tool
Here's the associated blog entry

12/01/2010

Task Sequence Environment not available

Problem:
Your deploying a Script which shell use Task Sequence Variables with SCCM.
There seems to be a problem when creating the object for the Task Sequence Environment CreateObject("Microsoft.SMS.TSEnvironment").
You may also get the following error: Error 429 - ActiveX component can't create object.

Solution:
Make sure that your really running the script within a Task Sequence. Also check that you have created a Task Sequence Advertisement (Advertise Task Sequence).

9/27/2010

NULL Values in SCCM Hardware Inventory

I tried to capture the value of the registry key HKLM\SOFTWARE\YOURCOMPANY\Department from all systems independed of the OS architecture. I had the problem that the inventory delivered NULL Values in the database, although everything seemed to be configured correctly. The problem occurred very sporadic. Sometimes a system delivered a value and sometimes it didn’t.
Here’s my basic configuration.mof and sms_def.mof

configuration.mof
#pragma namespace ("\\\\.\\root\\cimv2")
#pragma deleteclass("CustomHINV86", NOFAIL)
[DYNPROPS]
class CustomHINV86
{
[key] string KeyName = "";
string Department;
};

[DYNPROPS]
instance of CustomHINV86
{
KeyName="CustomHINV";
[PropertyContext("localHKEY_LOCAL_MACHINE\\SOFTWARE\\YOURCOMPANYDepartment"),
Dynamic, Provider("RegPropProv")]
Department;
};

#pragma namespace ("\\\\.\\root\\cimv2")
#pragma deleteclass("CustomHINV64", NOFAIL)
[DYNPROPS]
class CustomHINV64
{
[key] string KeyName = "";
string Department;
};

[DYNPROPS]
instance of CustomHINV64
{
KeyName="CustomHINV";
[PropertyContext("localHKEY_LOCAL_MACHINE\\SOFTWARE\\YOURCOMPANYDepartment"),
Dynamic, Provider("RegPropProv")]
Department;
};



sms_def.mof
#pragma namespace ("\\\\.\\root\\cimv2\\SMS")
#pragma deleteclass("CustomHINV86", NOFAIL)
[SMS_Report(TRUE),
SMS_Group_Name("Your Custom HINV"),
SMS_Class_ID("YOURCOMPANYYour Custom HINV1.0"),
SMS_Context_1 ("__ProviderArchitecture=32uint32"),
SMS_Context_2 ("__RequiredArchitecture=trueboolean")]

Class CustomHINV86: SMS_Class_Template
{
[SMS_Report(TRUE),key] string KeyName;
[SMS_Report(TRUE)] String Department;
};

#pragma namespace ("\\\\.\\root\\cimv2\\SMS")
#pragma deleteclass("CustomHINV64", NOFAIL)
[SMS_Report(TRUE),
SMS_Group_Name("Your Custom HINV"),
SMS_Class_ID("YOURCOMPANYYour Custom HINV1.0"),
SMS_Context_1 ("__ProviderArchitecture=64uint32"),
SMS_Context_2 ("__RequiredArchitecture=trueboolean")]

Class CustomHINV64: SMS_Class_Template
{
[SMS_Report(TRUE),key] string KeyName;
[SMS_Report(TRUE)] String Department;
};


The result was the following entry in the SQL table
ResourceIDGroupIDRevisionIDAgentIDTimeStampDepartment0KeyName0
3313114.09.2010 16:59:19NULLCustomHINV


That’s the problem!
As the hardware inventory listed above is designed for both OS architecture, there should be created two entries in the table. I then compared those entries with some working one, from another customization and saw the differences.
The entries were overwritten, as the column KeyName0 is not unique. This also explained the sporadic behavior. Depending on which inventory entry (32bit or 64 bit) was written into the database in which order, the first one was overwritten.
The correct entries for a 64 bit system should look like this:
ResourceIDGroupIDRevisionIDAgentIDTimeStampDepartment0KeyName0
3313114.09.2010 16:59:19NULLCustomHINV86
3313114.09.2010 16:59:19MyValueCustomHINV64


So the result was to customize the configuration.mof file the get unique KeyName0 columns. Here it is:
#pragma namespace ("\\\\.\\root\\cimv2")
#pragma deleteclass("CustomHINV86", NOFAIL)
[DYNPROPS]
class CustomHINV86
{
[key] string KeyName = "";
string Department;
};

[DYNPROPS]
instance of CustomHINV86
{
KeyName="CustomHINV86";
[PropertyContext("localHKEY_LOCAL_MACHINE\\SOFTWARE\\YOURCOMPANYDepartment"),
Dynamic, Provider("RegPropProv")]
Department;
};


#pragma namespace ("\\\\.\\root\\cimv2")
#pragma deleteclass("CustomHINV64", NOFAIL)
[DYNPROPS]
class CustomHINV64
{
[key] string KeyName = "";
string Department;
};

[DYNPROPS]
instance of CustomHINV64
{
KeyName="CustomHINV64";
[PropertyContext("localHKEY_LOCAL_MACHINE\\SOFTWARE\\YOURCOMPANYDepartment"),
Dynamic, Provider("RegPropProv")]
Department;
};

8/25/2010

SCCM PatchMgmt - Custom Reports

Here are some SCCM Patch Managenment Reports which i've created for my customers

Maintenance/Patch - Windows and each Computer

select
v_Collection.Name as CollectionName,
v_ServiceWindow.Description,
v_FullCollectionMembership.Name as Computername
from v_ServiceWindow
inner join v_FullCollectionMembership on (v_FullCollectionMembership.CollectionID = v_ServiceWindow.CollectionID)
inner join v_Collection on (v_Collection.CollectionID = v_FullCollectionMembership.CollectionID)
order By v_Collection.Name



Required Updates for a specific Computer

select
v_R_System.name0 as 'Computername',
v_UpdateInfo.Title as 'Updatename',
v_StateNames.Statename
from
v_StateNames,
v_Update_ComplianceStatusAll
Inner Join v_R_System On (v_R_System.ResourceID = v_Update_ComplianceStatusAll.ResourceID)
Inner Join v_UpdateInfo On (v_UpdateInfo.CI_ID = v_Update_ComplianceStatusAll.CI_ID)
where
v_StateNames.TopicType = 500 and
v_StateNames.StateID = v_Update_ComplianceStatusAll.Status and
v_R_System.name0 = @Computername and
Statename = 'Update is required'


Count of Required Updates for all Computers

select
v_R_System.Name0 as 'Computername',
Count(v_StateNames.Statename) as 'Required Updates'
from
v_StateNames,
v_Update_ComplianceStatusAll
Inner Join v_R_System On (v_R_System.ResourceID = v_Update_ComplianceStatusAll.ResourceID)
Inner Join v_UpdateInfo On (v_UpdateInfo.CI_ID = v_Update_ComplianceStatusAll.CI_ID)
where
v_StateNames.TopicType = 500 and
v_StateNames.StateID = v_Update_ComplianceStatusAll.Status and
v_StateNames.Statename = 'Update is required'
Group By v_R_System.Name0


Number of Computers requiring an Update

select
v_UpdateInfo.Title as 'Updatename',
Count(v_R_System.name0) as 'Count of Computers'
from
v_StateNames,
v_Update_ComplianceStatusAll
Inner Join v_R_System On (v_R_System.ResourceID = v_Update_ComplianceStatusAll.ResourceID)
Inner Join v_UpdateInfo On (v_UpdateInfo.CI_ID = v_Update_ComplianceStatusAll.CI_ID)
where
v_StateNames.TopicType = 500 and
v_StateNames.StateID = v_Update_ComplianceStatusAll.Status and
Statename = 'Update is required'
Group By v_UpdateInfo.Title
Order By 'Count of Computers' DESC


Assinged number of Updates for each Computer

select
v_R_System.Name0 as Computername,
Count(v_UpdateInfo.Title) as Updates
from v_UpdateState_Combined
inner join v_R_System on (v_R_System.ResourceID = v_UpdateState_Combined.ResourceID)
inner join v_CIAssignmentToCI on (v_CIAssignmentToCI.CI_ID = v_UpdateState_Combined.CI_ID)
inner join v_CIAssignment on (v_CIAssignment.AssignmentID = v_CIAssignmentToCI.AssignmentID)
inner join v_UpdateInfo on (v_UpdateInfo.CI_ID = v_UpdateState_Combined.CI_ID)
inner join v_StateNames on (v_StateNames.TopicType = v_UpdateState_Combined.StateType)
where
v_StateNames.StateID = v_UpdateState_Combined.StateID and
v_StateNames.StateName not like 'Update is not required'
Group by v_R_System.Name0


Assinged Updates and current State for a specific Computer

select
v_R_System.Name0 as 'Computername',
v_UpdateInfo.Title as 'UpdateDescription',
v_StateNames.StateName as 'State'
from v_UpdateState_Combined, v_CIAssignmentToCI,v_CIAssignment, v_UpdateInfo,v_StateNames, v_R_System
where v_R_System.Name0 = @MACHINENAME and
v_UpdateState_Combined.ResourceID = v_R_System.ResourceID and
v_UpdateState_Combined.CI_ID = v_CIAssignmentToCI.CI_ID and
v_CIAssignmentToCI.AssignmentID = v_CIAssignment.AssignmentID and
v_UpdateState_Combined.CI_ID = v_UpdateInfo.CI_ID and
v_UpdateState_Combined.StateType = v_StateNames.TopicType and
v_UpdateState_Combined.StateID = v_StateNames.StateID and
v_StateNames.StateName not like 'Update is not required'
Order By UpdateDescription


Assinged Updates in an UpdateList and current State for a specific Computer

select distinct v_R_System.Name0,v_StateNames.StateName, v_UpdateInfo.Title
from v_AuthListInfo,v_CIRelation,v_UpdateState_Combined,v_StateNames,v_UpdateInfo,v_R_System
where v_AuthListInfo.CI_ID = v_CIRelation.FromCIID and
v_CIRelation.ToCIID = v_UpdateInfo.CI_ID and
v_UpdateState_Combined.CI_ID = v_CIRelation.ToCIID and
v_StateNames.TopicType = v_UpdateState_Combined.StateType and
v_StateNames.StateID = v_UpdateState_Combined.StateID and
v_R_System.ResourceID = v_UpdateState_Combined.ResourceID and
v_R_System.Name0 = @COMPUTERNAME and
v_AuthListInfo.Title = @UPDATELIST and
v_StateNames.StateName not like 'Update is not required'


Number of assinged Updates in an UpdateList and current State for all Computers

select v_R_System.Name0 as Computername , v_StateNames.StateName ,count(v_UpdateInfo.Title) as UpdateCount, v_AuthListInfo.Title as Updatelist
from v_AuthListInfo
inner join v_CIRelation On (v_CIRelation.FromCIID = v_AuthListInfo.CI_ID)
inner join v_UpdateInfo On (v_UpdateInfo.CI_ID = v_CIRelation.ToCIID)
inner join v_CIAssignmentToCI on (v_CIAssignmentToCI.CI_ID = v_CIRelation.ToCIID)
inner join v_CIAssignmentTargetedMachines on (v_CIAssignmentTargetedMachines.AssignmentID = v_CIAssignmentToCI.AssignmentID)
inner join v_R_System on (v_R_System.ResourceID = v_CIAssignmentTargetedMachines.ResourceID)
inner join v_UpdateState_Combined on (v_UpdateState_Combined.CI_ID = v_CIRelation.ToCIID)
inner join v_StateNames on (v_StateNames.StateID = v_UpdateState_Combined.StateID)
where v_AuthListInfo.Title = @UPDATELIST and
v_UpdateState_Combined.ResourceID = v_R_System.ResourceID and
v_StateNames.TopicType = v_UpdateState_Combined.StateType
and v_StateNames.StateName not like 'Update is not required'
group by v_R_System.Name0, v_StateNames.StateName, v_AuthListInfo.Title
order by v_R_System.Name0


Number of Updates in an Update Deployment

select AssignmentName as Deployment,Count(CI_ID) as UpdateCount
from v_CIAssignment
Inner Join v_CIAssignmentToCI On (v_CIAssignmentToCI.AssignmentID = v_CIAssignment.AssignmentID)
where AssignmentName Like 'Update%'
Group By AssignmentName
order By AssignmentName


All Updates in an Updatelist

select distinct v_UpdateInfo.Title
from v_AuthListInfo,v_CIRelation,v_UpdateInfo
where v_AuthListInfo.CI_ID = v_CIRelation.FromCIID and
v_CIRelation.ToCIID = v_UpdateInfo.CI_ID and
v_AuthListInfo.Title = @UPDATELIST
order by v_UpdateInfo.Title