Submitting discovery records to an SMS 2003 MP using the MP API


There are several supported ways to add data to the SMS database but they typically come from an SMS 2003 advanced client.  If you want to add data to an existing client without having to make the agent on the client send it up, or add data to the database for a non-SMS client, then you can use the MP API.


The SMS SDK contains documentation and some sample code for submitting data via the MP API.  There are some other things you’ll need to do in order to get the MP API working with SMS 2003 SP1 and above.


Below is an example of using the MP API to submit discovery data to a Management Point.  This data gets converted from XML to a DDR by the MP, then moved to the site server, and finally loaded into the database.  Keep in mind that the discovery data I’m loading into the database in this example is the minimum so much more can be added.  Also, you can create your own GUIDs using CoCreateGUID().


Here are some tips when developing/testing submitting discovery data using the MP API.
–  Enable verbose and debug logging on the SMS 2003 Management Point you’re working with.
–  Have an SMS 2003 advanced client already submitting data to this MP.
–  Turn on report archival on the advanced client so you can see what a properly formatted XML looks like.
–  View the following logs in this order: IIS logs (shows IIS handling the message), ccmisapi.log (SMS ISAPI filter), MP_Ddr.log (converts the XML to a DDR), mpfdm.log (moves the DDR from the MP to site server), and ddm.log (loads the DDR into the database).
–  Make sure you’ve installed the hotfix from KB917873 onto your SDK.  This article is not published as of 06/28/2006 but the hotfix is still available by calling Microsoft support.
–  Don’t forget to set the Byte Order Mark in your XML attachment.
–  Make sure you call SetClientID, which is only usable if you’ve included the .h/.c files from KB917873 in your project.

–  If the program fails when running on the MP with http:// in front of the MP Name, try running it without the http:// (this goes through COM instead of IIS).

// DDRExample.cpp : Defines the entry point for the console application.
// Example of how to submit discovery data using the MP API
// This is a standard C++ Win32 console application created
// using the wizard in Visual Studio and adding MFC support
// Rslaten 06/2006

#include “stdafx.h”
#include “DDRExample.h”
#include “SmsMsgAPI.h”
#include “SmsMsgAPI_i.c”
#include “strsafe.h”

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define MAX_BUFFER_LENGTH 0x1000

CWinApp theApp;

using namespace std;

// Macros to perform error handling and make the code cleaner.
#define _BEGIN \
HRESULT __hrRetVal = S_OK; \
{ \

#define _END \
} \
__End: \

#define _RETVAL __hrRetVal

#define _CHECKHR(expr) if(FAILED(expr)) { __hrRetVal=expr; goto __End; }

//Main
int wmain( int argc, wchar_t *argv[], wchar_t *envp[] )
{
        int nRetCode = 0;

// initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
_tprintf(_T(“Fatal Error: MFC initialization failed\n”));
nRetCode = 1;
}
else
{

		if (argc < 3)
{
                        printf(“Usage:DDRExample.exe <http://mpname> <sitecode> \n”);
}
else
{
                        static WCHAR DDRBody[MAX_BUFFER_LENGTH];
                        static WCHAR DDRAttach[MAX_BUFFER_LENGTH];
                        ISmsMessaging *pMessaging = NULL;
                        ISmsMessage *pRequest = NULL;
                        ISmsMessage *pReply = NULL;
                        ISmsMessage2 *pRequest2 = NULL;
                        WCHAR *pszRequestMessage = NULL;
                        WCHAR *pszReplyMessage = NULL;
                        WCHAR *sMPName = NULL;
                        WCHAR *sSiteCode = NULL;
                        LPCWSTR sClientID = NULL;
                        LPCWSTR sReportID = NULL;
                        LPCWSTR sEndPoint = NULL;
                        WCHAR *pszTrackingID = NULL;

                        //Set variables
                        sReportID = L”{00000000-0000-0000-0000-000000000003}”;
                        sEndPoint = L”MP_DdrEndpoint”;
                        sClientID = L”GUID:8081C07C-4674-48E4-A525-5CAD419A4EB3″;
                        sMPName = argv[1];
                        sSiteCode = argv[2];
               

                        CoInitialize(NULL);

                        //Build Report Header for DDR
                        wcscat(DDRBody,L”<ReportHeader>”);
                        wcscat(DDRBody,L”<Identification>”);
                        wcscat(DDRBody,L”<Machine>”);
                        wcscat(DDRBody,L”<ClientID>GUID:8081C07C-4674-48E4-A525-5CAD419A4EB3</ClientID>”);
                        wcscat(DDRBody,L”</Machine>”);
                        wcscat(DDRBody,L”</Identification>”);
                        wcscat(DDRBody,L”</ReportHeader>”);

   
   //Build Message Attachment for DDR
   DDRAttach[0] = 0xFEFF; //Add BOM (This is very important)
   wcscat(DDRAttach,L”<?xml version=’1.0′ encoding=’UTF-16′?>”);
   wcscat(DDRAttach,L”<Report>”);
   wcscat(DDRAttach,L”<ReportHeader>”);
   wcscat(DDRAttach,L”<Identification>”);
   wcscat(DDRAttach,L”<Machine>”);
   wcscat(DDRAttach,L”<ClientInstalled>1</ClientInstalled>”);
   wcscat(DDRAttach,L”<ClientType>1</ClientType>”);
   wcscat(DDRAttach,L”<ClientID>GUID:8081C07C-4674-48E4-A525-5CAD419A4EB3</ClientID>”);
   wcscat(DDRAttach,L”<ClientVersion>1.0</ClientVersion>”);
   wcscat(DDRAttach,L”<NetBIOSName>MPAPITestClient</NetBIOSName>”);
   wcscat(DDRAttach,L”<CodePage>437</CodePage>”);
   wcscat(DDRAttach,L”<SystemDefaultLCID>1033</SystemDefaultLCID>”);
   wcscat(DDRAttach,L”</Machine>”);
   wcscat(DDRAttach,L”</Identification>”);
   wcscat(DDRAttach,L”<ReportDetails>”);
   wcscat(DDRAttach,L”<ReportContent>Inventory\\x0020Data</ReportContent>”);
   wcscat(DDRAttach,L”<ReportType>Full</ReportType>”);
   wcscat(DDRAttach,L”<Date>20060123125307.000000+000</Date>”);
   wcscat(DDRAttach,L”<Version>6.0</Version>”);
   wcscat(DDRAttach,L”<Format>1.0</Format>”);
   wcscat(DDRAttach,L”</ReportDetails>”);
   wcscat(DDRAttach,L”<InventoryAction ActionType=\”Predefined\”>”);
   wcscat(DDRAttach,L”<InventoryActionID>{00000000-0000-0000-0000-000000000003}</InventoryActionID>”);
   wcscat(DDRAttach,L”<Description>Discovery</Description>”);
   wcscat(DDRAttach,L”<InventoryActionLastUpdateTime>20050913162047.000000+000</InventoryActionLastUpdateTime>”);
   wcscat(DDRAttach,L”</InventoryAction>”);
   wcscat(DDRAttach,L”</ReportHeader>”);
   wcscat(DDRAttach,L”<ReportBody>”);
   wcscat(DDRAttach,L”<Instance ParentClass=\”SMS_Authority\” Class=\”SMS_Authority\” Namespace=”);
   wcscat(DDRAttach,L”\”\\\\MPAPITestClient\\ROOT\\ccm\” Content=\”New\”>”);
   wcscat(DDRAttach,L”<SMS_Authority>”);
   wcscat(DDRAttach,L”<Name>SMS:”);
   wcscat(DDRAttach, sSiteCode);
   wcscat(DDRAttach, L”</Name>”);
   wcscat(DDRAttach,L”</SMS_Authority>”);
   wcscat(DDRAttach,L”</Instance>”);
   wcscat(DDRAttach,L”</ReportBody>”);
   wcscat(DDRAttach,L”</Report>”);

_BEGIN

//Create root messaging object
_CHECKHR( ::CoCreateInstance(
CLSID_SmsMessaging,
NULL,
CLSCTX_INPROC,
IID_ISmsMessaging,
(LPVOID*)&pMessaging) );

//Create message object for request
_CHECKHR( pMessaging->CreateMessage(&pRequest) );

//Use the SetClientID method (must have .c/.h files from KB917873)
_CHECKHR( pRequest->QueryInterface(IID_ISmsMessage2, (void **) &pRequest2) );
_CHECKHR( pRequest2->SetClientID(sClientID) );

//Set Body (Header)
_CHECKHR( pRequest2->SetBodyFromString(DDRBody) );

//Get length of attachment
size_t pcch;
StringCchLengthW(DDRAttach,MAX_BUFFER_LENGTH,&pcch);
ULONG uSize = pcch * 2;

//Set Attachment
_CHECKHR( pRequest2->SetAttachmentFromBuffer(
sReportID,
(const BYTE*)DDRAttach,
uSize) );

//Set EndPoint
_CHECKHR( pRequest2->SetTargetEndpoint(sEndPoint) );

//Post Message
_CHECKHR( pMessaging->Post(sMPName, pRequest2, &pszTrackingID) );

_END

printf(“\n\n%s%X”, “Return Code=”, __hrRetVal);

if(pszTrackingID)
{
::CoTaskMemFree(pszTrackingID);
}
return S_OK;

pRequest2->Release();
}
}

CoUninitialize();
return nRetCode;
}

Comments (12)

  1. Submitting hardware inventory records to an SMS 2003 MP using the MP API

  2. There are several supported ways to add data to the SMS database but they typically come from an SMS…

  3. Submitting software inventory records to an SMS 2003 MP using the MP API

  4. eobiont says:

    How does this work when client inventory signing is turned on?

    I would like to submit signed DDRs to SMS 2003, but the DDR generation tools do not allow for client signing (as far as I can tell).

    Does the MP API offer a solution here?  I have been led to believe that signing will be a requirement in SMS v4.  What will we use to import inventory data then?

  5. rslaten says:

    Sorry for the delayed response (was on vacation).  If client signing is turned on then the MP will accept the report, but the server component (DDM in this case) will reject the DDR because it doesn’t contain a key.  If you turn on report archival (see my blog entry on logging) then you’ll see the EncodedSigningPublicKey property in the XML of the report on an advanced client.  This is also the same key located in WMI on an advanced client under CCMCCM_ClientSecurityInformation:EncodedSigningPublicKey.  Logically it should work if you include the appropriate key in the EncodedSigningPublicKey property in your report.

    Regarding SCCM 2007, I’m not sure at this point how the we are going to handle submitting reports directly to an MP.  I know there was some discussion around this a few months back so when I hear more I’ll provide an update.

  6. eobiont says:

    Regarding the EncodedSigningPublicKey

    This key in WMI on each machine also matches the data found in the SMS table ClientKeyData, which programmatically, is much easier to get at than the WMI repository on each individual machine.

    When I generate a DDR on a client, the key is also present in the XML.  The key matches in all three spots (SMS Table, local WMI, and Local XML).

    When I generate a Hardware delta, the key is not present in the XML generated by doing a "Hardware Inventory Cycle" — should it be? Or do only DDRs (Inventory action type 3) contain the signed data.

    The data doesn’t really seem signed per se.  Couldn’t I still edit the DDR .xml file and then submit it to the MP? When it comes to data signing I know very little, so maybe I’m misunderstanding the concept of data signing.  I thought signing data ensures both authenticity of the the object being signed and that it has not been tampered with in transport.

  7. eobiont says:

    I think that following down this path won’t help me in the situation.  I can get access to the clients public key as the client sends that to SMS and also stores it in the client’s WMI repository, but the XML files need to be signed using the client’s private key, and I don’t see how that can be found outside of the client and even then, who knows where it is stored?

    The public key can be used to check the validity of the XML file sent, but I don’t think it can be used to produce a signed XML document.  It looks like the XML documents held in the temp directory are created before they are signed.  The client probably signs the documents just as they are being sent.  That’s what threw me.  The XML files held because of the archive_reports.sms flag are not signed XML documents.

    Forcing signing is going to break a lot of functionality in SMS.  Extended AD discovery tools, many of the Quest and SMS expert products all rely on sending data to SMS.  I should think that a process running on the MP should be allowed to interact with the SMS processes in a trusted manner.

  8. rslaten says:

    Interacting with the MP isn’t the problem here, it’s the server side component that is looking for the key in the DDR.  I haven’t investigated other reports (like inventory) but in the case of a discovery report the MP converts the XML it gets from the client into a DDR and that DDR gets processed on the site server by the Discovery Data Manager component of the SMS Executive service.

    I tested the following and it worked:

    –  Used the DDR example I have in this blog entry

    –  Added an instance of the CCM_ClientSecurityInformation class to the ReportBody

    –  For EncodedSigningPublicKey value I pasted in one from an existing advanced client

    –  Compiled the code

    –  Ran it against an SMS 2003 SP1 Management Point (only site I had with client signing and encyption enabled on)

    –  The MP converted the XML to a DDR successfully

    –  DDM on the site server created the MPAPITestClient in the database successfully

  9. rslaten says:

    Hi "eobiont".  I received a response back from the person responsible for the SDK in SCCM 2007.  The method to submit discovery/inventory reports to SCCM 2007 is still under discussion and hasn’t been finalized yet.  We do have some questions for you though.  If you’re interested in helping us with this send me an e-mail at rslaten@microsoft.com.

    Thanks!

    Russ

  10. kahoyrup says:

    Hi – We’re testing out the ‘require signed data’ for client side inventory and are finding that unsigned data (at least for ddr’s) is still being processed by the MP and passed on to the discovery data manager.  Does anyone know how the discovery data manager checks the file prior to discarding — reference comment from earlier in this blog by russ Slaten… If client signing is turned on then the MP will accept the report, but the server component (DDM in this case) will reject the DDR because it doesn’t contain a key. —

    Thanks!

    Karen

  11. rslaten says:

    I’m not sure I really understand the question (If we know how DDM checks the file).  It simply looks for the key in the report before it will process the DDR.  Of course if you aren’t using the MP API then the advanced client puts this key in the report and DDM processes the inventory without an issue.  If you are using the MP API then you could always create a separate site for your MP API "clients" that doesn’t have client signing enabled.

  12. kahoyrup says:

    My apologies for not being clear.  I’m trying to gain some understanding on how this processing works.

    We are not using the MP API at this point.  We are seeing clients with unsigned ddr files from separate AD environments post to the MP and then the MP passes on to the DDM (even though the ddr is unsigned).

    We have run ccmsetup resetkeyinformation=true on the client posting the unsigned data.  We have then run an MP policy refresh cycle on the client, and forced a ddr.  The data still seems to get through to the DDM.

    Maybe I’m missing where the client gets the EncodedSigningPublic key information from.

    Thanks again.