SDK: In-depth sample on how to use the client messaging SDK–now with ISV proxy registration capabilities
A few months back I posted an in-depth sample performing various functions with the client messaging SDK. I’ve updated the sample to provide the ability to perform ISV proxy registration as well to make it easy to validate basic ISV proxy scenarios.
The sample is basically the same as it was originally but with an additional command, “I” which enables ISV proxy mode. If you type “I” you will need to enter the GUID of the ISV proxy client and then send a registration request. The client GUID will be assigned on behalf of the ISV proxy:
Here’s the updated code for quick viewing, and as always, a .cs file is attached at the end of the post.
1: using System;
2: using System.Diagnostics;
3: using System.Diagnostics.CodeAnalysis;
4: using System.Globalization;
5: using System.IO;
6: using Microsoft.ConfigurationManagement.Messaging.Framework;
7: using Microsoft.ConfigurationManagement.Messaging.Messages;
8: using Microsoft.ConfigurationManagement.Messaging.Sender.Http;
9:
10: namespace Microsoft.ConfigurationManagement.Messaging.Tools.InteractiveSdkSample
11: {
12: internal class SampleProgram
13: {
14: private static readonly HttpSender Sender = new HttpSender();
15: private static readonly ConsoleTraceListener TraceListener = new ConsoleTraceListener();
16: private static MessageCertificateX509 certificate;
17: private static SmsClientId clientId;
18: private static bool compression;
19: private static bool encryption;
20: private static SmsClientId isvProxyId;
21: private static bool fileLogging;
22: private static string mpHostname;
23: private static bool replyCompression;
24: private static string siteCode;
25: private static bool superVerbose;
26: private static bool verbose;
27:
28: /// <summary>
29: /// Sets the logging parameters
30: /// </summary>
31: private static void ChangeLogging()
32: {
33: if (true == superVerbose)
34: {
35: MessagingTrace.TraceSwitch.Level = TraceLevel.Verbose;
36: }
37: else if (true == verbose)
38: {
39: MessagingTrace.TraceSwitch.Level = TraceLevel.Info;
40: }
41: else
42: {
43: MessagingTrace.TraceSwitch.Level = TraceLevel.Warning;
44: }
45: }
46:
47: /// <summary>
48: /// Writes help to the console and exits
49: /// </summary>
50: private static void Help()
51: {
52: Console.WriteLine("{0} <MP Hostname> <SiteCode> [-v|-vv]", Environment.GetCommandLineArgs()[0]);
53: Console.WriteLine("This program requires a certificate called MixedModeTestCert.pfx with a password of \"test\"");
54: Console.WriteLine("-v: verbose -vv: super-verbose");
55: Console.WriteLine("step# is a number for the step to execute");
56: WriteSteps();
57: Environment.Exit(1);
58: }
59:
60: /// <summary>
61: /// Performs initialization, should only be run once
62: /// </summary>
63: private static void Initialize()
64: {
65: // Only dump readable characters since we're writing to the console
66: HexDumper.OnlyPrintReadable = true;
67:
68: // Initialize the sender and event handlers
69: Sender.OnRawDataSend += OnRawDataSend;
70: Sender.OnRawDataReceived += OnRawDataReceived;
71: Sender.OnSend += OnSend;
72: Sender.OnReceived += OnReceived;
73:
74: certificate = new MessageCertificateX509Volatile(Path.Combine(Environment.CurrentDirectory, "MixedModeTestCert.pfx"), "test");
75:
76: if (certificate == null)
77: {
78: Console.WriteLine(@"Could not load the test certificate");
79: Environment.Exit(1);
80: }
81:
82: Console.WriteLine(@"Using certificate for client authentication with thumbprint of '{0}'", certificate.Thumbprint);
83:
84: Trace.Listeners.Add(TraceListener);
85: ChangeLogging();
86: }
87:
88: /// <summary>
89: /// Input loop that runs when a specific step isn't specified at the command line
90: /// </summary>
91: [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
92: private static void InputLoop()
93: {
94: while (true)
95: {
96: WriteSteps();
97: Console.WriteLine(@"V -- change verbosity level");
98: Console.WriteLine(@"Q -- quit");
99: Console.WriteLine(@"C -- compression enable/disable");
100: Console.WriteLine(@"R -- reply compression enable/disable");
101: Console.WriteLine(@"E -- encryption enable/disable");
102: Console.WriteLine(@"L -- file logging enable/disable");
103: Console.WriteLine(@"I -- ISV proxy mode");
104:
105: Console.Write(@"Input: ");
106: ConsoleKeyInfo key = Console.ReadKey();
107: Console.WriteLine();
108:
109: switch (key.Key)
110: {
111: case ConsoleKey.L:
112: fileLogging = !fileLogging;
113: Console.WriteLine(@"Setting file logging enabled to: {0}", fileLogging);
114: break;
115: case ConsoleKey.R:
116: replyCompression = !replyCompression;
117: Console.WriteLine(@"Setting reply compression enabled to: {0}", replyCompression);
118: break;
119: case ConsoleKey.E:
120: encryption = !encryption;
121: Console.WriteLine(@"Setting encryption enabled to: {0}", encryption);
122: break;
123: case ConsoleKey.C:
124: compression = !compression;
125: Console.WriteLine(@"Setting compression enabled to: {0}", compression);
126: break;
127: case ConsoleKey.I:
128: Console.Write(@"Enter ISV proxy GUID: ");
129: string guid = Console.ReadLine();
130: isvProxyId = new SmsClientId(guid, true);
131: break;
132: case ConsoleKey.V:
133: if (verbose == false)
134: {
135: Console.WriteLine(@"Changing verbosity to VERBOSE");
136: verbose = true;
137: }
138: else if (superVerbose == false)
139: {
140: Console.WriteLine(@"Changing verbosity to SUPER VERBOSE");
141: superVerbose = verbose = true;
142: }
143: else
144: {
145: Console.WriteLine(@"Changing verbosity to quiet");
146: superVerbose = verbose = false;
147: }
148:
149: ChangeLogging();
150:
151: break;
152: case ConsoleKey.Q:
153: Console.WriteLine(@"Quitting");
154: Environment.Exit(0);
155: break;
156: case ConsoleKey.D1:
157: PerformOperation(Step.Register);
158: break;
159: case ConsoleKey.D2:
160: PerformOperation(Step.Ddr);
161: break;
162: case ConsoleKey.D3:
163: PerformOperation(Step.Hinv);
164: break;
165: case ConsoleKey.D4:
166: PerformOperation(Step.Sinv);
167: break;
168: case ConsoleKey.D5:
169: PerformOperation(Step.FileCollection);
170: break;
171: case ConsoleKey.D6:
172: PerformOperation(Step.MachinePolicy);
173: break;
174: case ConsoleKey.D7:
175: PerformOperation(Step.UserPolicy);
176: break;
177: default:
178: Console.WriteLine(@"Invalid input received: " + key.KeyChar);
179: break;
180: }
181: }
182: }
183:
184: /// <summary>
185: /// Main program section
186: /// </summary>
187: private static void Main(string[] args)
188: {
189: if (args.Length < 2)
190: {
191: Help();
192: }
193:
194: mpHostname = args[0];
195: siteCode = args[1];
196:
197: if (siteCode.Length != 3)
198: {
199: Console.WriteLine(@"Invalid site code {0} specified", siteCode);
200: Help();
201: }
202:
203: if (args.Length == 3)
204: {
205: if (args[2] == "-v")
206: {
207: verbose = true;
208: }
209: else if (args[2] == "-vv")
210: {
211: verbose = superVerbose = true;
212: }
213: else
214: {
215: Help();
216: }
217: }
218: else if (args.Length != 2)
219: {
220: Help();
221: }
222:
223: Initialize();
224: InputLoop();
225: }
226:
227: /// <summary>
228: /// Captures raw data received by HTTP sender and logs if superverbose is enabled
229: /// </summary>
230: private static void OnRawDataReceived(object sender, HttpSenderRawDataEventArgs e)
231: {
232: if (superVerbose == false && fileLogging == false)
233: {
234: return;
235: }
236:
237: if (superVerbose == true)
238: {
239: Console.WriteLine(@"Response raw data:");
240: HexDumper.DumpHexToConsole(e.GetRawData());
241: }
242:
243: if (fileLogging == true)
244: {
245: string dumpFile = string.Format(CultureInfo.InvariantCulture, "MsgBin_Received_{0}.bin", TimeHelpers.CurrentTimeAsUnixTime);
246: dumpFile = Path.Combine(Path.GetTempPath(), dumpFile);
247: Console.WriteLine("Dumping message to {0}", dumpFile);
248: File.WriteAllBytes(dumpFile, e.GetRawData());
249: }
250: }
251:
252: /// <summary>
253: /// Captures raw data sent by HTTP sender and logs if superverbose is enabled
254: /// </summary>
255: private static void OnRawDataSend(object sender, HttpSenderRawDataEventArgs e)
256: {
257: if (superVerbose == false && fileLogging == false)
258: {
259: return;
260: }
261:
262: if (true == superVerbose)
263: {
264: Console.WriteLine(@"Send raw data:");
265:
266: HexDumper.DumpHexToConsole(e.GetRawData());
267: }
268:
269: if (fileLogging == true)
270: {
271: string dumpFile = string.Format(CultureInfo.InvariantCulture, "MsgBin_Sent_{0}.bin", TimeHelpers.CurrentTimeAsUnixTime);
272: dumpFile = Path.Combine(Path.GetTempPath(), dumpFile);
273: Console.WriteLine("Dumping message to {0}", dumpFile);
274: File.WriteAllBytes(dumpFile, e.GetRawData());
275: }
276: }
277:
278: /// <summary>
279: /// Captures data received by HTTP sender and logs if superverbose is enabled
280: /// </summary>
281: private static void OnReceived(object sender, MessageSenderEventArgs e)
282: {
283: if (verbose == false)
284: {
285: return;
286: }
287:
288: Console.WriteLine(@"Received payload from MP:");
289: Console.WriteLine(e.Message.Body.Payload);
290: }
291:
292: /// <summary>
293: /// Captures data sent by HTTP sender and logs if superverbose is enabled
294: /// </summary>
295: private static void OnSend(object sender, MessageSenderEventArgs e)
296: {
297: if (verbose == false)
298: {
299: return;
300: }
301:
302: Console.WriteLine(@"Sending payload to MP:");
303: Console.WriteLine(e.Message.Body.Payload);
304:
305: if (e.Message.Attachments.Count > 0)
306: {
307: if (e.Message is ConfigMgrFileCollectionMessage ||
308: e.Message is ConfigMgrUploadRequest)
309: {
310: Console.WriteLine(@"Not writing attachments for {0} message to console", e.Message);
311: return;
312: }
313:
314: for (int i = 0; i < e.Message.Attachments.Count; i++)
315: {
316: Console.WriteLine(@"+======== ATTACHMENT #{0} (name: {1}) ========+", i + 1, e.Message.Attachments[i].Name);
317: Console.WriteLine(e.Message.Attachments[i].Body.Payload);
318: }
319: }
320: }
321:
322: /// <summary>
323: /// Performs an atomic operation
324: /// </summary>
325: [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
326: [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
327: private static void PerformOperation(Step step)
328: {
329: // Some steps require a valid client ID to work, this anonymous method ensures this.
330: Action checkIsClientIdPresent = () =>
331: {
332: if (null == clientId)
333: {
334: throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Cannot execute step #{0:d} {0} because client is not registered.", step));
335: }
336: };
337:
338: try
339: {
340: Console.WriteLine(@"===========================================================");
341: Console.WriteLine(@"Sending '{0}' message", step);
342: Console.WriteLine(@"===========================================================");
343: switch (step)
344: {
345: case Step.Register:
346:
347: ConfigMgrRegistrationRequest regRequest = new ConfigMgrRegistrationRequest();
348: regRequest.Settings.HostName = mpHostname;
349: regRequest.AddCertificateToMessage(certificate, CertificatePurposes.Signing | CertificatePurposes.Encryption);
350:
351: // regRequest.Discover();
352:
353: // Manual registration
354: // Required: must supply a NetBIOS hostname
355: regRequest.NetBiosName = "MyDummyHostName";
356:
357: // Required: must supply an agent identity (this is the name of your client)
358: regRequest.AgentIdentity = "MyCustomClient.exe";
359:
360: // Required: must supply a client FQDN
361: regRequest.ClientFqdn = "MyDummyHostName.MyDomain.net";
362:
363: if (null != isvProxyId)
364: {
365: Console.WriteLine("Registering as ISV proxy client");
366: regRequest.SigningSmsId = isvProxyId;
367: regRequest.AgentType = ClientRegistrationAgentType.IsvProxyClient;
368: }
369:
370: regRequest.Settings.Compression = (true == compression) ? MessageCompression.Zlib : MessageCompression.None;
371: regRequest.Settings.ReplyCompression = (true == replyCompression) ? MessageCompression.Zlib : MessageCompression.None;
372: clientId = regRequest.RegisterClient(Sender, TimeSpan.FromMinutes(5));
373: Console.WriteLine(@"Got SMSID from registration of: {0}", clientId);
374: break;
375:
376: case Step.Ddr:
377: checkIsClientIdPresent();
378:
379: ConfigMgrDataDiscoveryRecordMessage ddrMessage = new ConfigMgrDataDiscoveryRecordMessage();
380: ddrMessage.Settings.HostName = mpHostname;
381: ddrMessage.AddCertificateToMessage(certificate, CertificatePurposes.Signing | CertificatePurposes.Encryption);
382: ddrMessage.SmsId = clientId;
383: ddrMessage.SiteCode = siteCode;
384: ddrMessage.ADSiteName = "MyADSiteName";
385: ddrMessage.DomainName = "MyDomain.net";
386: ddrMessage.NetBiosName = "MyDummyHostName";
387: ddrMessage.ClientVersion = new ClientVersionV5();
388: ddrMessage.Discover();
389: ddrMessage.Settings.Compression = (true == compression) ? MessageCompression.Zlib : MessageCompression.None;
390: ddrMessage.Settings.Security.EncryptMessage = encryption;
391: ddrMessage.SendMessage(Sender);
392: break;
393:
394: case Step.Hinv:
395: checkIsClientIdPresent();
396:
397: ConfigMgrHardwareInventoryMessage hinvMessage = new ConfigMgrHardwareInventoryMessage();
398: hinvMessage.Settings.HostName = mpHostname;
399: hinvMessage.AddCertificateToMessage(certificate, CertificatePurposes.Signing | CertificatePurposes.Encryption);
400: hinvMessage.SmsId = clientId;
401: hinvMessage.SiteCode = siteCode;
402: hinvMessage.NetBiosName = "MyDummyHostName";
403: hinvMessage.DomainName = "MyDomain.net";
404: hinvMessage.Settings.Compression = (true == compression) ? MessageCompression.Zlib : MessageCompression.None;
405: hinvMessage.Settings.Security.EncryptMessage = encryption;
406: hinvMessage.AddInstancesToInventory(WmiClassToInventoryReportInstance.WmiClassToInventoryInstances(@"root\cimv2", "Win32_LogicalDisk", @"root\cimv2\sms", "SMS_LogicalDisk"));
407: hinvMessage.AddInstancesToInventory(WmiClassToInventoryReportInstance.WmiClassToInventoryInstances(@"root\cimv2", "Win32_Processor", @"root\cimv2\sms", "SMS_Processor"));
408: hinvMessage.AddInstancesToInventory(WmiClassToInventoryReportInstance.WmiClassToInventoryInstances(@"root\cimv2", "Win32_SystemDevices", @"root\cimv2\sms", "SMS_SystemDevices"));
409: hinvMessage.SendMessage(Sender);
410: break;
411:
412: case Step.Sinv:
413: checkIsClientIdPresent();
414:
415: ConfigMgrSoftwareInventoryMessage sinvMessage = new ConfigMgrSoftwareInventoryMessage();
416: sinvMessage.Settings.HostName = mpHostname;
417: sinvMessage.AddCertificateToMessage(certificate, CertificatePurposes.Signing | CertificatePurposes.Encryption);
418: sinvMessage.SmsId = clientId;
419: sinvMessage.SiteCode = siteCode;
420: sinvMessage.NetBiosName = "MyDummyHostName";
421: sinvMessage.Settings.Compression = (true == compression) ? MessageCompression.Zlib : MessageCompression.None;
422: sinvMessage.Settings.Security.EncryptMessage = encryption;
423: sinvMessage.AddDirectoryFilesToInventory(Environment.ExpandEnvironmentVariables(@"%WINDIR%\system32"), "*.exe", true, true);
424: InventoryInstanceElementFileSystemFile fakeFile = new InventoryInstanceElementFileSystemFile();
425: fakeFile.LastWriteDate = TimeHelpers.Epoch.AddDays(-200);
426: fakeFile.FileVersion = "1.0";
427: fakeFile.FileDescription = "Fake File";
428: fakeFile.CompanyName = "Fake Company";
429: fakeFile.FileName = "FakeFile.EXE";
430: fakeFile.FilePath = Environment.ExpandEnvironmentVariables(@"%SYSTEMDRIVE%\MyFakeFile.EXE");
431: fakeFile.ProductName = "Fake Product";
432: fakeFile.ProductVersion = "1.0";
433: fakeFile.Size = 1024;
434: sinvMessage.AddFileSystemFileInstanceToInventory(fakeFile);
435: sinvMessage.SendMessage(Sender);
436: break;
437:
438: case Step.FileCollection:
439: checkIsClientIdPresent();
440:
441: ConfigMgrFileCollectionMessage fcMessage = new ConfigMgrFileCollectionMessage();
442: fcMessage.Settings.HostName = mpHostname;
443: fcMessage.Settings.BitsUpload = true;
444: fcMessage.Discover();
445: fcMessage.AddCertificateToMessage(certificate, CertificatePurposes.Signing | CertificatePurposes.Encryption);
446: fcMessage.SmsId = clientId;
447: fcMessage.SiteCode = siteCode;
448: fcMessage.Settings.Compression = (true == compression) ? MessageCompression.Zlib : MessageCompression.None;
449: fcMessage.Settings.Security.EncryptMessage = encryption;
450: fcMessage.AddDirectoryFilesToCollection(Environment.ExpandEnvironmentVariables(@"%WINDIR%\system32"), "*.log", true, true);
451: fcMessage.SendMessage(Sender);
452: break;
453:
454: case Step.UserPolicy:
455: checkIsClientIdPresent();
456: ConfigMgrPolicyAssignmentRequest userPolicyMessage = new ConfigMgrPolicyAssignmentRequest();
457: userPolicyMessage.AddCertificateToMessage(certificate, CertificatePurposes.Signing | CertificatePurposes.Encryption);
458: userPolicyMessage.Settings.HostName = mpHostname;
459: userPolicyMessage.ResourceType = PolicyAssignmentResourceType.User;
460: userPolicyMessage.Settings.Security.AuthenticationScheme = AuthenticationScheme.Ntlm;
461: userPolicyMessage.Settings.Security.AuthenticationType = AuthenticationType.WindowsAuth;
462: userPolicyMessage.SmsId = clientId;
463: userPolicyMessage.SiteCode = siteCode;
464: userPolicyMessage.Discover();
465: userPolicyMessage.SendMessage(Sender);
466: userPolicyMessage.Settings.Security.EncryptMessage = encryption;
467: userPolicyMessage.Settings.ReplyCompression = (true == replyCompression) ? MessageCompression.Zlib : MessageCompression.None;
468: userPolicyMessage.Settings.Compression = (true == compression) ? MessageCompression.Zlib : MessageCompression.None;
469: break;
470:
471: case Step.MachinePolicy:
472: checkIsClientIdPresent();
473: ConfigMgrPolicyAssignmentRequest machinePolicyMessage = new ConfigMgrPolicyAssignmentRequest();
474: machinePolicyMessage.Settings.HostName = mpHostname;
475: machinePolicyMessage.AddCertificateToMessage(certificate, CertificatePurposes.Signing | CertificatePurposes.Encryption);
476: machinePolicyMessage.Settings.Security.EncryptMessage = encryption;
477: machinePolicyMessage.Settings.Compression = (true == compression) ? MessageCompression.Zlib : MessageCompression.None;
478: machinePolicyMessage.Settings.ReplyCompression = (true == replyCompression) ? MessageCompression.Zlib : MessageCompression.None;
479: machinePolicyMessage.ResourceType = PolicyAssignmentResourceType.Machine;
480: machinePolicyMessage.SmsId = clientId;
481: machinePolicyMessage.SiteCode = siteCode;
482: machinePolicyMessage.Discover();
483: machinePolicyMessage.SendMessage(Sender);
484:
485: break;
486: }
487:
488: Console.WriteLine(@"===========================================================");
489: Console.WriteLine(@"Successfully sent '{0}' message!", step);
490: Console.WriteLine(@"===========================================================");
491: }
492: catch (Exception e)
493: {
494: e.RethrowCriticalException();
495: Console.WriteLine();
496: MessagingTrace.TraceException("Got unexpected exception while executing messaging step #{0:d}: '{0}'", e, step);
497: Console.WriteLine(@"===========================================================");
498: Console.WriteLine(@"Failed to send '{0}' message!", step);
499: Console.WriteLine(@"===========================================================");
500: }
501: }
502:
503: /// <summary>
504: /// Writes the various steps that can be executed with this program to the console
505: /// </summary>
506: private static void WriteSteps()
507: {
508: Console.WriteLine(@"1 -- register client");
509: Console.WriteLine(@"2 -- send DDR");
510: Console.WriteLine(@"3 -- send hardware inventory");
511: Console.WriteLine(@"4 -- send software inventory");
512: Console.WriteLine(@"5 -- send collected files");
513: Console.WriteLine(@"6 -- request machine policy");
514: Console.WriteLine(@"7 -- request user policy");
515: }
516:
517: #region Nested type: Step
518:
519: /// <summary>
520: /// Steps supported by this sample
521: /// </summary>
522: private enum Step
523: {
524: /// <summary>
525: /// None (implementation implies manual selection loop)
526: /// </summary>
527: None = 0,
528:
529: /// <summary>
530: /// Register client
531: /// </summary>
532: Register = 1,
533:
534: /// <summary>
535: /// Send DDR
536: /// </summary>
537: Ddr = 2,
538:
539: /// <summary>
540: /// Send HINV
541: /// </summary>
542: Hinv = 3,
543:
544: /// <summary>
545: /// Send SINV
546: /// </summary>
547: Sinv = 4,
548:
549: /// <summary>
550: /// Send collected files
551: /// </summary>
552: FileCollection = 5,
553:
554: /// <summary>
555: /// Request machine policy
556: /// </summary>
557: MachinePolicy = 6,
558:
559: /// <summary>
560: /// Request user policy
561: /// </summary>
562: UserPolicy = 7,
563: }
564:
565: #endregion
566: }
567: }
Download the code sample: program.cs
Comments
- Anonymous
September 10, 2013
Very interesting... Now that I have registered by client has an ISVProxyClient how does that impact sending back say inventory? - Anonymous
September 17, 2013
Every client registered through the ISV Proxy has its own unique SMSID. The way to think of the ISV Proxy is it's a 1:n (1 certificate to many clients) rather than a typical client that's a 1:1 (1 certificate to 1 client) mapping. So, as long as you send the right hardware inventory for the right SMSID, it will be reflected like a regular client. - Anonymous
March 04, 2015
The comment has been removed