In most cases, it’s possible to upgrade scripts by replacing Azure AD cmdlets with cmdlets from the Microsoft Graph PowerShell SDK. As we prepare for the launch of the Office 365 for IT Pros (2023 Edition) eBook, we’re going through all the Azure AD and MSOL code examples in the book to replace as many as possible with either Microsoft Graph API queries or SDK cmdlets. Microsoft publishes a useful cmdlet map to help developers match old cmdlets with suitable replacements.
Sometimes, it’s not yet possible to replace a cmdlet because Microsoft doesn’t yet provide a direct equivalent in the Graph. In these instances, we’re leaving the older code in place because it will continue to work. When a suitable replacement is available, we’ll update the book with new Graph-based code.
It’s Good to Use Photos with Azure AD Accounts
We recommend that tenant administrators add photos for all Azure AD accounts, both regular user accounts and guest accounts. Having a face to recognize makes it easier to appreciate who’s involved in sharing a document or participating in a channel conversation in Teams. Tenant administrators are busy people, and even if they devote time to “guest hygiene” (cleaning up unwanted or obsolete guest accounts), they might not get around to adding photos for guest accounts via the Azure AD admin center or PowerShell. This is why it’s good if guest accounts can update their own photos.
In April 2021, I wrote about how to use cmdlets from the Azure AD PowerShell module to update the photo for your Azure AD guest account in another Microsoft 365 tenant. It’s a relatively straightforward procedure that’s facilitated by the way the Azure AD module works. Once you have a connection to a tenant, you can work with accounts and other objects the signed-in account can access. In this instance, your guest account.
Time moves on and it was time to upgrade the example showing how guests can upload their own photos. Unhappily, although the Set-MgUserPhotoContent cmdlet is available to replace the Set-AzureADUserThumbNailPhoto cmdlet, the technique of connecting to a target tenant with a guest account to update the account photo doesn’t work. At least, it doesn’t work unless the target tenant meets specific criteria.
Experimenting with the Microsoft Graph PowerShell SDK
I experimented with several target tenants where I have guest accounts to see what’s possible. The Connect-MgGraph cmdlet is happy to connect to a tenant and you can sign in with your guest account. At this point, things go wrong. Once you connect with the Azure AD module, you can update the guest account as described in the post referenced above.
However, the Microsoft Graph takes a more restrictive approach to permissions (or scope). Administrators must grant consent to the service principal used for interactive sessions with the Microsoft Graph PowerShell SDK for the permissions to interact with user accounts. In this case, consent must be in place for the User.ReadWrite.All permission before it’s possible to update a user account (or guest account) with a photo. Interactive sessions with the PowerShell SDK use delegated permissions, so even though the permission is User.ReadWrite.All (implying access to all mailboxes), the Graph constrains the scope of the permission to the signed-in user.
Our renowned technical editor, Vasil Michev, thought that he had solved the problem and published a note to that effect. Unhappily, further investigation proved that using the Microsoft Graph PowerShell SDK to connect to a target tenant to update the photo for a guest account only works if the service principal for the Microsoft Graph PowerShell enterprise app (application id 14d82eec-204b-4c2f-b7e8-296a70dab67e) has consent for the User.ReadWrite.All permission.
The Service Principal Question
Meeting these requirements means that someone has run Connect-MgGraph at some time in the past in the target tenant. This action creates the service principal if it’s not already known to Azure AD. If the service principal doesn’t exist in the target tenant, Connect-MgGraph cannot proceed until an administrator signs in to create the service principal (Figure 1). After the creation of the service principal, it can receive consent to use permissions, including User.ReadWrite.All.
Figure 1: Azure AD prompts to create the service principal for the Microsoft Graph PowerShell SDK
Although these conditions might exist for some tenants, there’s no guarantee that the service principal exists and holds the permission. For example, connecting to the Microsoft tenant with a guest account reveals that the User.Read permission is available, but the User.ReadWrite.All permission is not.
In a nutshell, it’s possible to use the Microsoft Graph PowerShell SDK to update photos for guest accounts in other tenants, but only when the conditions are exactly right.
That Permissioned Service Principal
In closing, let me note once again the cumulative nature of the service principal used by the Microsoft Graph PowerShell SDK. If an administrator consents to a permission for use with the Graph SDK, that permission remains assigned to the service principal unless an administrator removes it. Over time, permissions accrue, and the service principal becomes highly permissioned. This makes it easy for tenant administrators to run SDK cmdlets in interactive sessions, but it’s possibly not what you want to happen. On the upside, interactive sessions use delegated permissions rather than application permissions, so the Graph won’t allow the signed-in user to access data that they couldn’t otherwise open. Which is nice to know.
{"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}