Now Possible to Make Teams Policy Assignments in Azure Automation Runbook
Microsoft released a new version (4.9) of the MicrosoftTeams PowerShell module on November 1. One of the changes made in this module is the upgrade of four policy management and assignment cmdlet sets so that they no longer have a dependency on the WinRM component. The policies include the Teams meeting and messaging policies, two of those most important and heavily used. The other two are the Teams feedback policy and the Teams voicemail policy.
Although many of the Teams PowerShell cmdlets are Graph-based, the Teams developers have been modernizing the older policy management cmdlets inherited from the Skype for Business Online connector. When modernized, cmdlets can run in environments like Azure Automation (this article explains the concept of using Teams PowerShell in Azure Automation). If you want to use Azure Automation with Teams, make sure that the service principals used for authentication have the necessary permissions.
Checking Teams Policy Assignments
Obviously, it’s a good thing for the Teams PowerShell cmdlets to support Azure Automation, but would you use these cmdlets in Azure Automation runbooks? I don’t think that you’d use Azure Automation for something like a bulk assignment of a Teams policy to user accounts. Teams already has its own bulk assignment mechanism.
The new capability is more likely to be used in periodic checks run against accounts to make sure that they have the right policy assignments. Given the number of Teams policies, it can be easy to miss out an inappropriate or erroneous assignment, so having a regularly-scheduled job to make sure that the right assignments are in place is a good idea. With the advent of the Teams Premium license, it might also be a way to make sure that these expensive add-ons ($10/user/month) are granted to the right accounts.
Example – Making Teams Policy Assignments for IT Department Members
To take a simple example, let’s assume that we want to assign specific meeting and messaging policies to members of the IT Department. The concept for the runbook is simple:
Find the users to process.
Grant the policies.
The Get-CsOnlineUser cmdlet from the Teams module hasn’t yet been modernized so it cannot be used with Azure Automation. Instead, we can use cmdlets from the Microsoft Graph PowerShell SDK or Exchange Online management modules to find target accounts. As I want to filter users based on department, I chose to use the Get-Recipient cmdlet.
The Grant-CsTeamsMeetingPolicy and Grant-CsTeamsMessagingPolicy are the two cmdlets needed to assign policies to the target accounts. Unhappily, I discovered that although the cmdlets work in an Azure Automation runbook, they don’t work when using a managed identity for authentication. Plan B duly ensued, and I reverted to fetching credentials for an account used for background processing from Azure Key Vault and used those credentials to connect to Teams instead. I guess you can’t expect perfection overnight.
Here’s the complete code:
# Connect to AzAccount to access Key Vault to fetch variables used by the script
$AzConnection = Connect-AzAccount -Identity | Out-Null
# Get username and password from Key Vault
$UserName = Get-AzKeyVaultSecret -VaultName "xxxxxx" -Name "AccountName" -AsPlainText
$UserPassword = Get-AzKeyVaultSecret -VaultName "xxxxxx" -name "AccountPassword" -AsPlainText
# Create credentials object from the username and password
[securestring]$SecurePassword = ConvertTo-SecureString $UserPassword -AsPlainText -Force
[pscredential]$UserCredentials = New-Object System.Management.Automation.PSCredential ($UserName, $SecurePassword)
Connect-MicrosoftTeams -Credential $UserCredentials
Connect-ExchangeOnline -ManagedIdentity -Organization office365itpros.onmicrosoft.com
[Array]$Users = Get-Recipient -RecipientTypeDetails UserMailbox -Filter {Department -eq 'IT'}
$Users | Format-Table DisplayName, WindowsLiveId -AutoSize
ForEach ($User in $Users) {
Write-Output ("Processing {0} with UPN {1} to assign IT messaging and meeting policies to account" -f $User.DisplayName, $User.WindowsLiveId)
Grant-CsTeamsMessagingPolicy -Identity $User.WindowsLiveId -Policy 'Advanced' -ErrorAction SilentlyContinue
Grant-CsTeamsMeetingPolicy -Identity $User.WindowsLiveId -Policy 'IT Department Meeting Policy' -ErrorAction SilentlyContinue
}
Figure 1 shows the output of a test run. This doesn’t really prove anything except that the Write-Output cmdlet worked!
Figure 1: Updating Teams policy assignments in Azure Automation
To make this runbook operational, we need to publish it and then link the runbook to an Azure automation schedule so that it’s run based on whatever period is deemed appropriate. Monthly seems like a good idea.
Making sure that these kind of boring, repetitive, but valuable checks are done is a good example of where Azure Automation is very useful. Sure, you could run the script interactively once a month, but you’d have to remember to do it and it’s a task likely to be interrupted by more essential (and interesting) work, so it might not get done.
Inching Forward
Microsoft is gradually modernizing the Teams PowerShell module. As each update appears, new possibilities for operational automation emerge. In this instance, we’re making sure that user accounts have the right Teams messaging and meeting policies. I’m sure you can come up with other examples.
Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.
Have updatet to Teams module to 4.9 but my job does not have enough permissions?
Connecting to teams with service principal in a Run Book:
Connect-MicrosoftTeams -ApplicationId $appIdTeams -CertificateThumbprint $TeamsCertThumbPrint -TenantId $tenantId
Service Principial has delegated full Teams admin permission.
When running runbook it fails with permissions denied.
If I connect to teams with stored credentials, it works.
Connect-MicrosoftTeams -Credential $Cred
Error on following commands:
Grant-CsTeamsComplianceRecordingPolicy -Identity $User.UserPrincipalName -PolicyName $null
Grant-CsTeamsComplianceRecordingPolicy -Identity $User.UserPrincipalName -PolicyName $PolicyName
Do you know if there are any other rights I need? It works with global admin rights, but not if I give global admin rights to the app I have.
I reported in the article that I had to revert to using stored credentials to make the cmdlets work. I’m sure that Microsoft will improve this area (eventually), but for now it looks like this is what’s needed to make the policy management cmdlets work.
{"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}
Have updatet to Teams module to 4.9 but my job does not have enough permissions?
Connecting to teams with service principal in a Run Book:
Connect-MicrosoftTeams -ApplicationId $appIdTeams -CertificateThumbprint $TeamsCertThumbPrint -TenantId $tenantId
Service Principial has delegated full Teams admin permission.
When running runbook it fails with permissions denied.
If I connect to teams with stored credentials, it works.
Connect-MicrosoftTeams -Credential $Cred
Error on following commands:
Grant-CsTeamsComplianceRecordingPolicy -Identity $User.UserPrincipalName -PolicyName $null
Grant-CsTeamsComplianceRecordingPolicy -Identity $User.UserPrincipalName -PolicyName $PolicyName
Do you know if there are any other rights I need? It works with global admin rights, but not if I give global admin rights to the app I have.
I reported in the article that I had to revert to using stored credentials to make the cmdlets work. I’m sure that Microsoft will improve this area (eventually), but for now it looks like this is what’s needed to make the policy management cmdlets work.
How would you target a 365 group for this? -Filter MemberOfGroup -eq ‘MyTestGroup’?
I don’t understand the question. How do you want to use a Microsoft 365 group?
How can I target all users that are part of a security group using this script?