Skip to content

Commit

Permalink
Updated invoke admin-service output format
Browse files Browse the repository at this point in the history
  • Loading branch information
Mayyhem committed Jan 31, 2024
1 parent 72cfde4 commit 12becaa
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 61 deletions.
21 changes: 5 additions & 16 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ static void Main(string[] args)
//invoke adminService
var invokeAdminService = new Command("admin-service", "Invoke an arbitrary CMPivot query against a collection of clients or a single client via AdminService\n" +
" Requirements:\n" +
" - Full Administrator\n" +
" - \"Read\" and \"Run CMPivot\" permissions for the \"Collections\" scope\n" +
" - https://learn.microsoft.com/en-us/mem/configmgr/core/servers/manage/cmpivot#permissions\n" +
" Examples:\n" +
" - SharpSCCM_merged.exe invoke admin-service -q \"Device\" -r 16777211\n" +
Expand All @@ -624,27 +624,21 @@ static void Main(string[] args)
invokeAdminService.AddOption(new Option<string>(new[] { "--collection-id", "-i" }, "The collectionId to point the query to. (e.g., SMS00001 for all systems collection)") { Arity = ArgumentArity.ExactlyOne });
invokeAdminService.Add(new Option<string>(new[] { "--resource-id", "-r" }, "The unique ResourceID of the device to point the query to. Please see command \"get resourceId\" to retrieve the ResourceID for a user or device") { Arity = ArgumentArity.ExactlyOne });
invokeAdminService.Add(new Option<string>(new[] { "--delay", "-d" }, "Seconds between requests when checking for results from the API,(e.g., --delay 5) (default: requests are made every 5 seconds)"));
invokeAdminService.Add(new Option<string>(new[] { "--retries", "-re" }, "The total number of attempts to check for results from the API before a timeout is thrown.\n (e.g., --retries 5) (default: 5 attempts will be made before a timeout"));
invokeAdminService.Add(new Option<string>(new[] { "--retries", "-re" }, "The total number of attempts to check for results from the API before timing out.\n (e.g., --retries 5) (default: 5 attempts will be made before a timeout"));
invokeAdminService.Add(new Option<bool>(new[] { "--json", "-j" }, "Get JSON output"));
invokeAdminService.Handler = CommandHandler.Create(
async (string smsProvider, string siteCode, string query, string collectionId, string resourceId, string delay, string retries, bool json) =>
{
string[] delayTimeoutValues = new string[] { "5", "5" };
if (delay != null)
{
if (delay.Length < 1 || !uint.TryParse(delay, out uint value) || value == 0)
{
Console.WriteLine("\r\n[!] Please check your syntax for the --delay parameter (e.g., --delay 5)\r\n[!] Leave blank for the default 5 seconds wait before each attempt to retrieve results");
return;
}
else
{
delayTimeoutValues[0] = delay;
}
delayTimeoutValues[0] = delay;
}
if (retries != null)
{
Expand All @@ -653,10 +647,7 @@ static void Main(string[] args)
Console.WriteLine("\r\n[!] Please check your syntax for the --retries parameter (e.g., --retries 5)\r\n[!] Leave blank for a default of 5 retries before reaching a timeout");
return;
}
else
{
delayTimeoutValues[1] = retries;
}
delayTimeoutValues[1] = retries;
}
if ((string.IsNullOrEmpty(query)) || (string.IsNullOrEmpty(collectionId) && string.IsNullOrEmpty(resourceId)))
{
Expand All @@ -671,10 +662,8 @@ static void Main(string[] args)
if (smsProvider == null)
{
(smsProvider, _) = ClientWmi.GetCurrentManagementPointAndSiteCode();
}
await AdminService.Main(smsProvider, siteCode, query, collectionId, resourceId, delayTimeoutValues, json);
await AdminService.CheckOperationStatusAsync(smsProvider, siteCode, query, collectionId, resourceId, delayTimeoutValues, json);
}
});

Expand Down
93 changes: 48 additions & 45 deletions lib/AdminService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public static async Task<string> CheckOperationStatusAsync(string managementPoin
{
// Here we display the output as JSON after the user supplies the required flag
Console.WriteLine($"\r---------------- CMPivot data ------------------\r");
return jsonObject.ToString();
Console.WriteLine(jsonObject.ToString());
}

// The file content query returns files line by line. We use this to output lines together
Expand All @@ -203,6 +203,7 @@ public static async Task<string> CheckOperationStatusAsync(string managementPoin
return null;
}

Console.WriteLine("----------------------------------------");
foreach (JObject valueObject in values)
{
JArray results = (JArray)valueObject["Result"];
Expand All @@ -229,47 +230,34 @@ public static async Task<string> CheckOperationStatusAsync(string managementPoin

// For other queries, print each key value pair
JObject parsedJsonA = JObject.Parse(jsonBody);
JArray valuesA = (JArray)parsedJsonA["value"];

if (valuesA == null) return null;

Console.WriteLine("----------------------------------------");
foreach (JObject valueObject in valuesA)
// Check if 'value' is a JArray or a JObject and process accordingly
var valueToken = parsedJsonA["value"];
if (valueToken is JArray valuesA)
{
// Check if 'Result' exists, is not null, and is a JArray
if (valueObject["Result"] is JArray results)
Console.WriteLine("----------------------------------------");

// Process each value in the array
foreach (JObject valueObject in valuesA)
{
foreach (JObject result in results)
{
string device = result["Device"]?.ToString();
Console.WriteLine("Device: " + device);

foreach (var property in result)
{
string key = property.Key;
JToken value = property.Value;

if (key != "Device") // Skip Device as it's already printed
{
Console.WriteLine($"{key}: {value}");
}
}
Console.WriteLine("----------------------------------------");
}
ProcessResult(valueObject);
}
}
return jsonObject.ToString();
}
else
{
string fail = "";
if (status == 404)
else if (valueToken is JObject valueObject)
{
//Note we also get a 404 while results are not ready so when this message is for when 404 is received after we got an operationId and the timeout limit was reached
fail = $"[!] Received a 404 response after the set timeout was reached. It might mean that the device is not online or timeout value is too short. You can also try to retrieve results manually using the retrieved OperationId {opId}";
// Process the single value object
ProcessResult(valueObject);
}
return fail;
return string.Empty;
}

string fail = "";
if (status == 404)
{
//Note we also get a 404 while results are not ready so when this message is for when 404 is received after we got an operationId and the timeout limit was reached
fail = $"[!] Received a 404 response after the set timeout was reached. It might mean that the device is not online, the query returned an error, or timeout value is too short. You can also try to retrieve results manually using the retrieved OperationId {opId}";
}
return fail;
}

public static uint InitiateClientOperationExMethodCall(string query, string managementPoint, string sitecode, string CollectionName, string deviceId)
Expand Down Expand Up @@ -320,11 +308,9 @@ public static uint InitiateClientOperationExMethodCall(string query, string mana
Console.WriteLine("[+] Fallback Method call succeeded");
return returnValue;
}
else
{
Console.WriteLine("[!] Method call failed with error code {0}.", returnValue);
return 0;
}

Console.WriteLine("[!] Method call failed with error code {0}.", returnValue);
return 0;
}
catch (ManagementException e)
{
Expand All @@ -333,16 +319,33 @@ public static uint InitiateClientOperationExMethodCall(string query, string mana
}
}

// Entry point with arguments provided by user or defaults from command handler
public static async Task Main(string managementPoint, string sitecode, string query, string collectionName, string deviceId, string[] timeoutValues, bool json)
// Method to process each 'Result' in a 'value' object
public static void ProcessResult(JObject valueObject)
{
var CMPdata = await CheckOperationStatusAsync(managementPoint, sitecode, query, collectionName, deviceId, timeoutValues, json);
if (!string.IsNullOrWhiteSpace(CMPdata))
if (valueObject["Result"] is JArray results && results != null)
{
Console.WriteLine("\r" + CMPdata + "\r");
Console.WriteLine("----------------------------------------");

foreach (JObject result in results)
{
string device = result["Device"]?.ToString();
Console.WriteLine("Device: " + device);

foreach (var property in result)
{
string key = property.Key;
JToken value = property.Value;

// Skip Device as it's already printed
if (key != "Device")
{
Console.WriteLine($"{key}: {value}");
}
}
Console.WriteLine("----------------------------------------");
}
}
}

}
public class JsonResponse
{
Expand Down

0 comments on commit 12becaa

Please sign in to comment.