Assign the Teams Administrator Permission to Apps and Automation Accounts
Last week, I explained how to assign the Exchange.ManageAsApp permission to an Azure automation account. The permission allows an automation runbook to sign into Exchange Online using a system-assigned managed identity and run cmdlets in the Exchange Online management module. Essentially, the permission gives the automation account the right to act like an Exchange administrator who signs in interactively to run Exchange Online cmdlets. The upshot is that any script written for the Exchange Online module becomes a candidate to be run by Azure automation.
The same situation applies to the Teams PowerShell module. When someone runs the Connect-MicrosoftTeams cmdlet to connect to the Teams management endpoint in an interactive session, they can use all the rights and permissions held by their Azure AD account. If those rights and permissions include a role like Global administrator or Teams administrator, they can perform management operations for Teams at the tenant level.
Giving an App the Right to Act as an Administrator
To use the cmdlets in the Teams PowerShell module in an Azure automation runbook, the automation account must hold the permission to act as an administrator. This requirement applies to all methods of authentication used when apps use the module in background jobs, including when a runbook uses a managed identity.
A runbook connects to Teams with a managed identity like this:
Connect-MicrosoftTeams -Identity
When authenticating the connection, Azure AD evaluates the roles and permissions held by the automation account that owns the runbook. It’s equivalent to a user when they connect to Teams in an interactive PowerShell session. If the account holds a Teams administrator role, it can run cmdlets to update the Teams tenant configuration, alter policies, and update accounts.
Assigning the Teams Administrator Permission (Role)
In the previous article, we assigned the role to manage Exchange Online (which holds the Exchange.ManageAsApp permission) to the automation account. The same principle applies to Teams. Instead of assigning the role to allow the app to manage Exchange Online, we assign the role to allow it to manage Teams. As explained in the other article, the Azure AD admin center doesn’t support role assignment to automation accounts, so this must be done using PowerShell.
This code:
Connects to the Microsoft Graph.
Populates a variable with details of the service principal for the Skype and Teams Tenant Admin API app. This is an enterprise app installed into tenants to allow components like the Teams admin center to manage Teams. Its app id is always 48ac35b8-9aa8-4d74-927d-1f4a14a0b239.
Find the Application_access role in the set held by the Skype and Teams Tenant Admin API app.
Populate a variable with details of the automation account to use. In this example, the account is called TeamsAutomationAccount.
Populate the parameters to make the role assignment.
Run the New-MgServicePrincipalAppRoleAssignment cmdlet to assign the Application_access role to the service principal of the automation account.
Here’s the code:
Connect-MgGraph -Scopes AppRoleAssignment.ReadWrite.All
Select-MgProfile Beta
# Fetch details of the Teams management app
$TeamsApp = Get-MgServicePrincipal -Filter "AppId eq '48ac35b8-9aa8-4d74-927d-1f4a14a0b239'"
$AppPermission = $TeamsApp.AppRoles | Where-Object {$_.DisplayName -eq "Application_access"} # Create the payload for the assignment
$ManagedIdentityApp = Get-MgServicePrincipal -Filter "displayName eq 'TeamsAutomationAccount'"
$AppRoleAssignment = @{
"PrincipalId" = $ManagedIdentityApp.Id
"ResourceId" = $TeamsApp.Id
"AppRoleId" = $AppPermission.Id }
# Assign the role to the service principal for the managed identity.
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $ManagedIdentityApp.Id -BodyParameter $AppRoleAssignment
After the code runs, check the permissions for the automation account and you should find that the Teams administrative role is present (Figure 1).
Figure 1: The Teams administrative role assigned to the service principal for an automation account
Remember that the access granted only allows the automation account to run administrative operations. Other operations might need consent for a Teams Graph permission.
Working with Teams Policies
The current version (4.7) of the Teams PowerShell module doesn’t support using a managed identity to run the older cmdlets from the Skype for Business Connector (like Get-CsTeamsMessagingPolicy). This is because Microsoft needs to update these cmdlets to support managed identities.
If you need to run the older cmdlets in an automation runbook, you can sign into Teams with an account that holds the Teams administrator role. For instance, you could store the credentials for an account in Azure Key Vault and use those credentials with Connect-MicrosoftTeams.
Azure Automation and Teams
Using an automation account with scheduled runbooks is an excellent way to make sure that administrative jobs get run. Being able to use common Microsoft 365 PowerShell modules like Teams expands the scope of possible work. A managed identity relieves the need to maintain credentials and keeps things more secure. All in all, it’s a nice combination.
Learn more about how the Office 365 applications really work on an ongoing basis by subscribing to the Office 365 for IT Pros eBook. Our monthly updates keep subscribers informed about what’s important across the Office 365 ecosystem.
{"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}