Debug Microsoft Graph PowerShell SDK Cmdlets to Gain Insights into What They Do
A comment for my article about recent enhancements for the Microsoft Graph Explorer noted that while it was great to see the Graph Explorer generate PowerShell code snippets for requests it executes, it would be even nicer if the Graph Explorer supported “round tripping.” In this instance, that means users could send PowerShell commands to the Graph Explorer, which would then interpret the commands and generate the appropriate Graph API requests. It sounds like a great idea.
I contacted some of the folks working on the Microsoft Graph to see if this is possible. Although they couldn’t commit on such an implementation appearing in the future, I was told about a nice feature in the Microsoft Graph PowerShell SDK cmdlets. It doesn’t address the round-tripping request, but it’s a good thing to know none the less, especially if you’re grappling to understand how the SDK cmdlets work for tasks like user, group, or license management.
In a nutshell, if you add the –Debug parameter to any Microsoft Graph PowerShell SDK cmdlet, you’ll see exactly what the cmdlet does, including the Graph API request it runs to execute the command. This is a great way to gain insight into how these cmdlets work and also understand how to leverage Graph API requests.
Because many of its cmdlets are built on Graph APIs, the Debug parameter also works in the same manner for cmdlets from the Microsoft Teams PowerShell module. However, the Teams cmdlets do not output details about permissions and the older policy cmdlets that originated from the Skype for Business Online connector do not display Graph API URIs when they run.
Running with the Debug Parameter
Let’s take a basic example and run the Get-MgUser cmdlet to fetch details of all user accounts in a tenant:
When the cmdlet starts, it shows the context it will run under, including whether it’s an interactive session and the scopes (permissions) available to the command. You can get the same information by running the Get-MgContext cmdlet, but this is useful up-front knowledge.
In Figure 1 you can see that the service principal used by the Microsoft Graph PowerShell SDK has many permissions. This is the result of permission creep, the tendency of the service principal to accrue permissions over time due to testing different cmdlets. The existence of so many permissions makes it a bad idea to use the Microsoft Graph PowerShell SDK cmdlets interactively unless you know what you’re doing. In production, it’s best to use certificate-based authentication and a registered Azure AD app to limit the permissions available.
Figure 1: Debug Microsoft Graph PowerShell SDK cmdlets – the execution context
The Graph API request is now displayed. We can see that it looks for the top 100 matching items that satisfy the filter. In other words, return the first 100 Azure AD member accounts (Figure 2).
Figure 2: Debug Microsoft Graph PowerShell SDK cmdlets – the HTTP GET request
As you can see, running with Debug set, the cmdlet halts frequently to allow you to read what’s happened and understand if the command has any problems. If you want to see the cmdlet run as normal but with the diagnostic information, set the SDebugPreference variable from its default (SilentlyContinue) to Continue.
$DebugPreference="Continue"
To revert to normal operation, set $DebugPreference back to SilentlyContinue.
Pagination
Pagination is a concept that doesn’t really exist in PowerShell. Some cmdlets have a ResultSize parameter to control the number of items retrieved by a command, and some have an All parameter to tell the command to fetch everything. The Get-MgUser and Get-MgGroup cmdlets are examples of cmdlets that support an -All parameter.
Graph API requests limit the retrieval of data (usually to 100 or 200 items) to avoid issues caused by requests that might mistakenly look for tens of thousands of items. If more items exist, the application must make additional requests to fetch more pages of data until it has fetched all available items. Applications do this by following a nextlink (or skiptoken) link.
In Figure 3, we see a nextlink for the cmdlet to run to retrieve the next page of data. In this instance, I ran the Get-MgUser cmdlet with no filter, so more than 100 accounts are available, and this is what caused the Graph to respond with the first 100 accounts and the nextlink. In debug mode, you can pause after each page to see the results retrieved from the Graph.
Figure 3: Debug Microsoft Graph PowerShell SDK cmdlets – a nextlink to more data
Another Thing for the Administrator Toolbox
Facilities like the Debug parameter and the Graph X-ray tool help people to understand how the Graph APIs work. Knowing how the Graph functions is invaluable. Having an insight into how cmdlets work helps people develop better code and hopefully avoid bugs. At least, that’s the theory. Try out the Debug parameter with some Microsoft Graph PowerShell SDK cmdlets and see what you think.
Learn how to exploit the data available to Microsoft 365 tenant administrators like how to debug Microsoft Graph PowerShell SDK cmdlets through the Office 365 for IT Pros eBook. We love figuring out how things work.
2 Replies to “Use the Debug Parameter for Microsoft Graph PowerShell SDK Cmdlets to Expose Graph API Requests”
Great info Tony! I guess one thing worth mentioning (a downside when attempting to run the script unattended) is that the -Debug switch continuously prompts for each operation (command). Another way of getting the Debug output is to run the cmdlet without the -Debug switch, but setting the $DebugPreference variable to ‘Continue’ (default is ‘SilentlyContinue’). E.g.:
$DebugPreference=’Continue’
Get-MgUser -All -Filter “userType eq ‘Member'”
{"id":null,"mode":"button","open_style":"in_modal","currency_code":"EUR","currency_symbol":"\u20ac","currency_type":"decimal","blank_flag_url":"https:\/\/office365itpros.com\/wp-content\/plugins\/tip-jar-wp\/\/assets\/images\/flags\/blank.gif","flag_sprite_url":"https:\/\/office365itpros.com\/wp-content\/plugins\/tip-jar-wp\/\/assets\/images\/flags\/flags.png","default_amount":100,"top_media_type":"featured_image","featured_image_url":"https:\/\/office365itpros.com\/wp-content\/uploads\/2022\/11\/cover-141x200.jpg","featured_embed":"","header_media":null,"file_download_attachment_data":null,"recurring_options_enabled":true,"recurring_options":{"never":{"selected":true,"after_output":"One time only"},"weekly":{"selected":false,"after_output":"Every week"},"monthly":{"selected":false,"after_output":"Every month"},"yearly":{"selected":false,"after_output":"Every year"}},"strings":{"current_user_email":"","current_user_name":"","link_text":"Virtual Tip Jar","complete_payment_button_error_text":"Check info and try again","payment_verb":"Pay","payment_request_label":"Office 365 for IT Pros","form_has_an_error":"Please check and fix the errors above","general_server_error":"Something isn't working right at the moment. Please try again.","form_title":"Office 365 for IT Pros","form_subtitle":null,"currency_search_text":"Country or Currency here","other_payment_option":"Other payment option","manage_payments_button_text":"Manage your payments","thank_you_message":"Thank you for supporting the work of Office 365 for IT Pros!","payment_confirmation_title":"Office 365 for IT Pros","receipt_title":"Your Receipt","print_receipt":"Print Receipt","email_receipt":"Email Receipt","email_receipt_sending":"Sending receipt...","email_receipt_success":"Email receipt successfully sent","email_receipt_failed":"Email receipt failed to send. Please try again.","receipt_payee":"Paid to","receipt_statement_descriptor":"This will show up on your statement as","receipt_date":"Date","receipt_transaction_id":"Transaction ID","receipt_transaction_amount":"Amount","refund_payer":"Refund from","login":"Log in to manage your payments","manage_payments":"Manage Payments","transactions_title":"Your Transactions","transaction_title":"Transaction Receipt","transaction_period":"Plan Period","arrangements_title":"Your Plans","arrangement_title":"Manage Plan","arrangement_details":"Plan Details","arrangement_id_title":"Plan ID","arrangement_payment_method_title":"Payment Method","arrangement_amount_title":"Plan Amount","arrangement_renewal_title":"Next renewal date","arrangement_action_cancel":"Cancel Plan","arrangement_action_cant_cancel":"Cancelling is currently not available.","arrangement_action_cancel_double":"Are you sure you'd like to cancel?","arrangement_cancelling":"Cancelling Plan...","arrangement_cancelled":"Plan Cancelled","arrangement_failed_to_cancel":"Failed to cancel plan","back_to_plans":"\u2190 Back to Plans","update_payment_method_verb":"Update","sca_auth_description":"Your have a pending renewal payment which requires authorization.","sca_auth_verb":"Authorize renewal payment","sca_authing_verb":"Authorizing payment","sca_authed_verb":"Payment successfully authorized!","sca_auth_failed":"Unable to authorize! Please try again.","login_button_text":"Log in","login_form_has_an_error":"Please check and fix the errors above","uppercase_search":"Search","lowercase_search":"search","uppercase_page":"Page","lowercase_page":"page","uppercase_items":"Items","lowercase_items":"items","uppercase_per":"Per","lowercase_per":"per","uppercase_of":"Of","lowercase_of":"of","back":"Back to plans","zip_code_placeholder":"Zip\/Postal Code","download_file_button_text":"Download File","input_field_instructions":{"tip_amount":{"placeholder_text":"How much would you like to tip?","initial":{"instruction_type":"normal","instruction_message":"How much would you like to tip? Choose any currency."},"empty":{"instruction_type":"error","instruction_message":"How much would you like to tip? Choose any currency."},"invalid_curency":{"instruction_type":"error","instruction_message":"Please choose a valid currency."}},"recurring":{"placeholder_text":"Recurring","initial":{"instruction_type":"normal","instruction_message":"How often would you like to give this?"},"success":{"instruction_type":"success","instruction_message":"How often would you like to give this?"},"empty":{"instruction_type":"error","instruction_message":"How often would you like to give this?"}},"name":{"placeholder_text":"Name on Credit Card","initial":{"instruction_type":"normal","instruction_message":"Enter the name on your card."},"success":{"instruction_type":"success","instruction_message":"Enter the name on your card."},"empty":{"instruction_type":"error","instruction_message":"Please enter the name on your card."}},"privacy_policy":{"terms_title":"Terms and conditions","terms_body":null,"terms_show_text":"View Terms","terms_hide_text":"Hide Terms","initial":{"instruction_type":"normal","instruction_message":"I agree to the terms."},"unchecked":{"instruction_type":"error","instruction_message":"Please agree to the terms."},"checked":{"instruction_type":"success","instruction_message":"I agree to the terms."}},"email":{"placeholder_text":"Your email address","initial":{"instruction_type":"normal","instruction_message":"Enter your email address"},"success":{"instruction_type":"success","instruction_message":"Enter your email address"},"blank":{"instruction_type":"error","instruction_message":"Enter your email address"},"not_an_email_address":{"instruction_type":"error","instruction_message":"Make sure you have entered a valid email address"}},"note_with_tip":{"placeholder_text":"Your note here...","initial":{"instruction_type":"normal","instruction_message":"Attach a note to your tip (optional)"},"empty":{"instruction_type":"normal","instruction_message":"Attach a note to your tip (optional)"},"not_empty_initial":{"instruction_type":"normal","instruction_message":"Attach a note to your tip (optional)"},"saving":{"instruction_type":"normal","instruction_message":"Saving note..."},"success":{"instruction_type":"success","instruction_message":"Note successfully saved!"},"error":{"instruction_type":"error","instruction_message":"Unable to save note note at this time. Please try again."}},"email_for_login_code":{"placeholder_text":"Your email address","initial":{"instruction_type":"normal","instruction_message":"Enter your email to log in."},"success":{"instruction_type":"success","instruction_message":"Enter your email to log in."},"blank":{"instruction_type":"error","instruction_message":"Enter your email to log in."},"empty":{"instruction_type":"error","instruction_message":"Enter your email to log in."}},"login_code":{"initial":{"instruction_type":"normal","instruction_message":"Check your email and enter the login code."},"success":{"instruction_type":"success","instruction_message":"Check your email and enter the login code."},"blank":{"instruction_type":"error","instruction_message":"Check your email and enter the login code."},"empty":{"instruction_type":"error","instruction_message":"Check your email and enter the login code."}},"stripe_all_in_one":{"initial":{"instruction_type":"normal","instruction_message":"Enter your credit card details here."},"empty":{"instruction_type":"error","instruction_message":"Enter your credit card details here."},"success":{"instruction_type":"normal","instruction_message":"Enter your credit card details here."},"invalid_number":{"instruction_type":"error","instruction_message":"The card number is not a valid credit card number."},"invalid_expiry_month":{"instruction_type":"error","instruction_message":"The card's expiration month is invalid."},"invalid_expiry_year":{"instruction_type":"error","instruction_message":"The card's expiration year is invalid."},"invalid_cvc":{"instruction_type":"error","instruction_message":"The card's security code is invalid."},"incorrect_number":{"instruction_type":"error","instruction_message":"The card number is incorrect."},"incomplete_number":{"instruction_type":"error","instruction_message":"The card number is incomplete."},"incomplete_cvc":{"instruction_type":"error","instruction_message":"The card's security code is incomplete."},"incomplete_expiry":{"instruction_type":"error","instruction_message":"The card's expiration date is incomplete."},"incomplete_zip":{"instruction_type":"error","instruction_message":"The card's zip code is incomplete."},"expired_card":{"instruction_type":"error","instruction_message":"The card has expired."},"incorrect_cvc":{"instruction_type":"error","instruction_message":"The card's security code is incorrect."},"incorrect_zip":{"instruction_type":"error","instruction_message":"The card's zip code failed validation."},"invalid_expiry_year_past":{"instruction_type":"error","instruction_message":"The card's expiration year is in the past"},"card_declined":{"instruction_type":"error","instruction_message":"The card was declined."},"missing":{"instruction_type":"error","instruction_message":"There is no card on a customer that is being charged."},"processing_error":{"instruction_type":"error","instruction_message":"An error occurred while processing the card."},"invalid_request_error":{"instruction_type":"error","instruction_message":"Unable to process this payment, please try again or use alternative method."},"invalid_sofort_country":{"instruction_type":"error","instruction_message":"The billing country is not accepted by SOFORT. Please try another country."}}}},"fetched_oembed_html":false}
Great info Tony! I guess one thing worth mentioning (a downside when attempting to run the script unattended) is that the -Debug switch continuously prompts for each operation (command). Another way of getting the Debug output is to run the cmdlet without the -Debug switch, but setting the $DebugPreference variable to ‘Continue’ (default is ‘SilentlyContinue’). E.g.:
$DebugPreference=’Continue’
Get-MgUser -All -Filter “userType eq ‘Member'”
Yep, that’s certainly a point to consider!