MPElementsExtract: The Story and Use Cases (Part I)

After my first post about MPElementsExtract tool I was told that it’s pretty hard to understand why to use it, so I promised to write about use cases. So, here they are.

Scripts
Some years ago, when I started with my first OpsMgr management pack, I have noted that all my scripts have the same structure and some common parts. Generally speaking, almost all scripts looked like this:
' constants
Option Explicit
On Error Resume Next

SetLocale("en-us")
HandleErrorContinue("Cannot set en-us locale")

Const DEBUG_MODE = false
Const ManagementGroupName = "$Target/ManagementGroup/Name$"
Const ManagementGroupID = "$Target/ManagementGroup/Id$"
…

' call Main()
Dim WShell, FSO, Logger
CreateGlobalObjects()
Main()

' common functions
Sub CreateGlobalObjects()
…
End Sub

Sub HandleError(customMessage)
…
End Sub
…

' Registry helper
Class Registry
…
End Class

' WMI helper
Function WMIGetObject(ByVal sNamespace)
…
End Function
…

' SQL Helper
Class SQL
…
End Class

' Script-specific code
' Arguments:
'  0: …
'  1: …
'  2: …
Sub Main()
…
End Sub

Function SomeFunction(…)
…
End Function
…

 

So, every time I was adding a new feature or fixing a bug in some common routine, I had to copy-paste the code into lot of scripts. I was making mistakes when coping and pasting and was spending lot of time for debugging and fixing that as well.


Shortly it came to my mind, that it’ll be great if I could store common parts in one place (somewhere side-by-side with my MP)  and inject them into MP after modification. My idea was to simplify development of scripts embedded into MP. Here is the process I had in my mind:
  1. Preparation phase
    1. Extract scripts from existing MP (if you have one already – that was my case);
    2. Split scripts into subscripts to separate common and specific parts;
    3. Replace inner text of <ScriptBody> element with “place  mark”, making future embedding possible;
  1. Development phase
    1. Modify subscripts, stored side by side with MP and/or modify MP itself (anything but scripts!);
    2. Build: Embed modified subscripts into MP;
  1. Test phase
    1. Extract scripts (concatenated version) from MP built at previous step;
    2. Test scripts;
    3. Test MP.

This is where MPElementsExtract starts.

Extracting and splitting scripts

First of all I had to decide what to use as a delimiter for  script parts. Here I have to note, that I haven’t invented the bicycle, the idea came when I was looking through Microsoft’s MPs, especially SQL one. So I choose to use the same approach: a line containing  5 # symbols (#####) is considered to be a first line of subscript, all symbols, after 5 # symbols are considered to be a file name for a subscript.

So all my scripts had to look like this:
' ##### common\common.vbs
' constants
Option Explicit
On Error Resume Next

SetLocale("en-us")
HandleErrorContinue("Cannot set en-us locale")

Const DEBUG_MODE = false
Const ManagementGroupName = "$Target/ManagementGroup/Name$"
Const ManagementGroupID = "$Target/ManagementGroup/Id$"
…

' call Main()
Dim WShell, FSO, Logger
CreateGlobalObjects()
Main()

' common functions
Sub CreateGlobalObjects()
…
End Sub

Sub HandleError(customMessage)
…
End Sub
…

' ##### common\registry.vbs
' Registry helper
Class Registry
…
End Class

' ##### common\wmi.vbs
' WMI helper
Function WMIGetObject(ByVal sNamespace)
…
End Function
…

' ##### common\sql.vbs
' SQL Helper
Class SQL
…
End Class

' ##### MyScriptName.vbs
' Script-specific code
' Arguments:
'  0: …
'  1: …
'  2: …
Sub Main()
…
End Sub

Function SomeFunction(…)
…
End Function
…

 

Next step was to decide what to use as a place mark. Since <ScriptBody> element may contain any text, I decided to use following format (don’t ask me why – I don’t remember 🙂 ):

 

{{path\file.ext}}

 

Obviously, MP will contain only placeholders in all  <ScriptBody> elements and all subscripts will be saved to disk after preparation phase. Here is the example of MP code (I have skipped some values to shorten the text)…

 

… before:
<DataSourceModuleType … >

…

…

$Config/IntervalSeconds$
$Config/SyncTime$
MyScriptName.vbs
…

' ##### common\common.vbs
' constants
…
' call Main()
…
' common functions
…
' ##### common\registry.vbs
' Registry helper
…
' ##### common\wmi.vbs
' WMI helper
…
' ##### common\sql.vbs
' SQL Helper
…
' ##### MyScriptName.vbs
' Script-specific code
…

$Config/TimeoutSeconds$

…

 

… and after:
…

…

$Config/IntervalSeconds$
$Config/SyncTime$
MyScriptName.vbs
…

{{common\common.vbs}}
{{common\registry.vbs}}
{{common\wmi.vbs}}
{{common\sql.vbs}}
{{MyScriptName.vbs}}

$Config/TimeoutSeconds$

…

 

Some important notes :
  1. Content of a subscript is respected. i.e. if we have several subscripts with the same name and content, we will not get duplicates on disk, but if content is different, we’ll get several files even if file name is the same;
  2. All file names are considered to be relative to extraction directory (please refer to original documentation for more details).
Embedding scripts
The process of embedding (AKA injection) scripts into MP is pretty much the same as extraction described above. The major difference is that we’re looking for place marks (one per line) and, when found, replace them with the content stored side-by-side with the MP. Thus, if there were no modifications to subscripts, we’ll get the same MP as we had before scripts extraction after running MPElementsExtract in embedding mode.
Extraction modes /ex[tract] vs /p[utplacemarks]
As you may note, there are 2 steps in development process (see above ) which require scripts extraction: 1.1-3 (Preparation phase) and 3.1 (Test phase). The purpose and requirements are a bit different here, though extraction logics is the same. That’s why 2 extraction modes have been implemented. Here is the comparison table:
Action
/ex
/p
Extracts entire script from MP and saves it to disk
yes
no
Extracts entire script from MP, splits it into subscripts and saves subscripts to disk
yes
yes
Replaces subscripts with place marks
no
yes
Modifies MP source and saves modified version to disk
no
Yes


Important notes on MP Modules configuration, cookdown and other MP Authoring-related things
MPElementsExtract has no idea about what is an MP module or cookdown. It just looks for some elements with specific names in the XML document.
Generally speaking, the only purpose of this tool is:
  1. To extract scripts (and other supported content) and save them side-by-side with MP;
  2. Replace text of element with appropriate place marks;
  3. Replace place marks with content stored on the disk to build an MP;
That’s it!


Reporting-related elements

Leave a Comment