“Root element is missing” error during SMA Runbook starup

This error is most often seen because of nested runbook invocation from inline or function context. Such action is obviously not permitted. We can call Inline or Function from Workflow, but in other direction we have to use one of solutions described here.
I have just met another cause of such error.
In powershell, there is nomally available such syntax:

1
$var = (get-service)[0]

It is using result of expression within brackets as a table, what is indexed without creation of intermediate variable.
Based on my expierience and some tests, I can state that this syntax is causing error “Root element is missing”

Export/Import ADCS Certificate Templates in Windows 2012

Hello,

Todays a few words about importing and exporting certificate template data with Active Directory.
This data is stored within Configuration partition of Active Directory and therefore it is common for whole Active Directory Forest.
But there are also situation, when you would like to move them between forest, for example from pre-production environment to your production one.
It can be done on two different ways. First is obvious, but dirty, and sometime can lead to serious problems with moved Items.
But after Windows 2008 R2 was released, there was introduced new feature – Windows AD CS Web Enrollment Services.
It can be used for cross forest CA publication with only External Forest AD Trust enabled.
More details about those protocols can be found here.
On the mentioned site, there are two great scripts written in powershell, what would export and import Certificate Template data to and from xml files in MS-XCEP format.
However, Those scripts have two bugs and one issue within when running on Windows 2012:

  1. There is some kind of error during using them as normal cmd-lets on Windows 2012. Instead, you should comment out first function declaration and cmd-let binding declaration. After this modification, you can use scripts as normal with additional input, as required.
  2. Export function in one place tries to figure out, if there is one of attributes. The line:
    1
    $superseded = if ($temp.Settings.SupersededTemplates -eq 0) {

    should look as follow:

    1
    $superseded = if ($temp.Settings.SupersededTemplates.Length -eq 0) {

    Just replace it, and scripts will run very well on W2k12.

  3. Last thing is those scripts are depending on external powershell module. As far, as I know, it is possible to remove this dependecy, but it will subject of next post.

Mentioned earlier dirty method is old ldifde command with required flags for export:

1
ldifde -m -v -d cn=%Template1%,%LDAP% -f %Template1%.ldf

Most important part of this command is -m switch, which will result in “forest free” ldf output.
For import you should use the similar command:

1
 ldifde -i -k -f %Template1%.ldf

And this should work from the begining, starting from Windows 2000 Version.

How to remotelly trigger run advertisement on SCCM 2012 Client

Hello Everybody,

there is many questions on the web about remote invoking SCCM actions on the client via scripting.
Within official SCCM 2012 SDK, there is absolutelly nothing about such client actions. Within the 2007 version it is demonstrated, but with local script and invoking the CPAppletMgr in conjuction with UIResourceMgr COM object class. But configuration of DCOM for accepting remote request for this objects is at least tricky and useless because it is dummy to beleve this classes will not change in the future.
Most natural way for SCCM to do this is create apropriative WMI calls.
And here Microsoft makes new troubles for all of us, scripting guys – in new SCCM 2012 SDK is dry description of classes witin CCM namespace. But also absolutelly nothing as notes for using this.
Finally I have found brilliant tool: SMSCLICTR, what encapsulates all of settings within simple to use .NET assemblies. This can also be used within Powershell, but sometimes usage such external modules is prohibited.
So I have made own piece of code in Powershell for doing this manually.

  1. First of all I had to enforce current Machine policy refresh. I is done by following function:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function ForceMachinePolicyRefresh
    {
    param($clientname, $username, $userpass, $PolicyID)
    $ms = new-object system.management.managementscope
    $ms.Path = "\\$clientname\root\CCM"
    $ms.options.username = $username
    $ms.options.password = $userpass
    $mc = new-object system.management.managementclass($ms, 'SMS_Client', $null)
    $mc.invokeMethod("TriggerSchedule", $PolicyID)
    return $mc
    }

    There are several things to explain within this code:

    • It connects to namespace with explicity manner, without any casting, because of credentials passing. This code can be invoked only remotelly, because setting $ms.options.username is supported only with remote connection.
    • It is general function for enforcing any scheduled action. So it can be used not only for Machine policy refresh, but also applying changes within localconfig, as you can see later.
    • Last thing connected with this small piece of code is fact it can be used as a base for manipulating any of published method within SMS_Client WMI class. Now the MS refrence from SDK can be helpfull.
  2. So, when we have this defined we can go further. Now the function for invoking exact advertisement of specified Package. Function takes all necessary arguments:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    function InvokeOptionalAdvertisement
    {
    Param($clientname, $username, $userpass, $advertID, $packID)
    $mc = ForceMachinePolicyRefresh -clientname $clientname -username $username -userpass $userpass -PolicyID "{00000000-0000-0000-0000-000000000021}"
    $ms = new-object system.management.managementscope
    $ms.path = "\\$clientname\root\ccm\policy\Machine\ActualConfig"
    $ms.options.username = $username
    $ms.options.userpass = $userpass
    $query =new-object System.Management.ObjectQuery
    $query.QueryString = "Select * From CCM_SoftwareDistribution where ADV_AdvertisementID = '$advertID' and PKG_PackageID = '$packID'"
    $searcher = new-object system.management.managementobjectsearcher($query)
    $searcher.Scope = $ms
    $advs = $searcher.Get
    $enum = $advs.GetEnumerator()
    $enum.MoveNext()
    $adv = $enum.Current
    $adv.SetPropertyValue("ADV_RepeatRunBehavior", "RerunAlways")
    $adv.SetPropertyValue("ADV_MandatoryAssignments", "True")
    $adv.Put()
    $query1 = new-object System.Management.ObjectQuery
    $query1.QueryString = "Select ScheduledMessageID FROM CCM_Scheduler_ScheduledMessageID like '" + $adv.ADV_AdvertisementID + "-" + $adv.PKG_PackageID + "%'"
    $searcher1 = new-object System.management.managementobjectsearcher($query1)
    $searcher1.scope = $ms
    $scheds = $searcher1.Get()
    $scheds | Foreach-Object { $mc[1].invokeMethod("TriggerSchedule", $_.ScheduledMessageID) }
    return $adv
    }

    So now a word of comment for this function:

    • On the beginning we invoke machine policy refresh with our previously defined function
    • Next we define new management scope with namespace of actual config, what is used by CCM.
    • After that we need the ObjectQuery instance for encapsulating correct WMI Query. This query select all CCM_SoftwareDistribution objects, what matches our conditions
    • by usage of searcher object we obtain all required object to $advs variable
    • trick with enumerator and him Current property gives us only one object, instead of containing it collection
    • now we do main Job. We modify two properties, what ensures that optional assigment is now mandatory and will run on next schedule of this Advertisement
    • last part of the script gets proper objects of schedulers for our Advertisement and Package.
    • finally we trigger the schedules and task sequence runs properly.