Stream Doesn’t Support Office 365 Retention Policies
Usage of Microsoft Stream is very high at present, largely driven by storage of Teams meeting recordings and Teams Live Events. This is goodness, but the increased use is revealing some flaws in Stream, such as the lack of support for Office 365 retention policies. At their Microsoft Stream Under the hood session at the Ignite 2019 conference, the development group promised that Stream will support retention policies “in 2020.” The need to keep the service running at a time of high demand might have stopped progress in this space.
When a video owner deletes a video, it goes into the Stream recycle bin and remains there for 30 days. This period allows owners to recover videos deleted in error. After the period elapses, Stream removes the video and the content is irrecoverable.
Exploring the Stream Recycle Bin
Although Stream administrators can see videos deleted by anyone in the tenant in the Stream recycle bin (Figure 1), a danger exists that a video which should be kept might slip through the cracks and be deleted. People in the compliance world worry about this kind of thing because someone could try to remove evidence of a policy violation by deleting recordings of Teams meetings where misdoings were discussed.
Figure 1: Deleted videos in the Stream Recycle Bin
While waiting for Microsoft to bring Stream into the full Office 365 compliance framework (spanning retention policies, labels, and holds among other features), it’s possible to build a review mechanism to have someone check videos in the recycle bin to figure out if they should be kept. It’s a form of manual disposition, to use Microsoft’s term for a manual review of an item before it is disposed of.
To achieve the goal, I looked in the Office 365 audit log for StreamDeleteVideo events, which are logged when an owner deletes a video. It doesn’t take much to search for these events over the last 30 days and extract the relevant data, such as the name of the video and who deleted it.
Generating an HTML Report
After processing the audit events, I create an HTML report file (Figure 2). To help identify videos that are approaching deletion, the days since deletion and days remaining are calculated by subtracting the timestamp of the audit event from the current date. Someone creative could take other approaches to highlight entries in the list, such as using different colors for videos approaching their final deletion.
Figure 2: The HTML report about Stream deleted videos
Emailing the Report
Generating a report is one step, making sure that its content is reviewed and actioned is another. To help things along, the script emails a copy to someone selected to review videos and decide if any should be kept. Figure 3 shows the report as it appears in OWA.
Figure 3: The emailed report about Stream deleted videos viewed in OWA
Emailing reports is easy with the Send-MailMessage cmdlet. In this case, the HTML content is imported as the message body, we add message properties such as the recipients, sender, and subject, and send it off. Using Send-MailMessage in this way is a good example of the kind of script that will need to be updated when Microsoft removes basic authentication support for SMTP AUTH connections.
The script can be accessed in GitHub. Feel free to create your own version and let us know what improvements you make by posting a comment here.
The Office 365 for IT Pros eBook includes a full chapter about Stream. Like everything else in the book, the Stream content is reviewed and updated when changes happen.
Thank you for sharing this informations in your blog. I used the script from GitHub. I added this rows in before for getting the O365 credentials and to open a session to Exchange for Search-UnifiedAuditLog:
#Enter credentials and establish connection
$O365Cred = Get-Credential
In row 57 I added my MsgTo mail address with adding “”) #” before the “Change this …”. I deleted a video in my Stream tenant, waited about 2 hours to see the entry in the Security & Compliance Center audit logs and when I call your script I get the html mail including the informations you described! Wonderful!
# Make sure that we have credentials to send the message
If (!$O365Cred) {$O365Cred = Get-Credential}
# And that we’re connected to Exchange Online
Try { $OrgName = (Get-OrganizationConfig).Name }
Catch {
Write-Host “Your PowerShell session is not connected to Exchange Online.”
Write-Host “Please connect to Exchange Online using an administrative account and retry.”
Break }
It’s just a different way of attacking the problem. I wouldn’t connect to Exchange Online the way that you do. Instead, download the Exchange Online management module https://petri.com/exchange-online-powershell-module-v2 and use it instead. You can still get to the Search-UnifiedAuditLog cmdlet and you’re using modern authentication, etc.
I have a question, you said: “After the period elapses, Stream removes the video and the content is irrecoverable.”
What of I didn’t wait until the 30 days elapsed, and I deleted the video from Recycle bin, would the stream admin in my company be able to restore it after I deleted it permanently from recycle bin before 30 days elapsed?
{"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}
Thank you for sharing this informations in your blog. I used the script from GitHub. I added this rows in before for getting the O365 credentials and to open a session to Exchange for Search-UnifiedAuditLog:
#Enter credentials and establish connection
$O365Cred = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $O365Cred -Authentication Basic -AllowRedirection
Import-PSSession $Session -DisableNameChecking
In row 57 I added my MsgTo mail address with adding “”) #” before the “Change this …”. I deleted a video in my Stream tenant, waited about 2 hours to see the entry in the Security & Compliance Center audit logs and when I call your script I get the html mail including the informations you described! Wonderful!
I’m glad that it works for you.
The latest version has:
# Make sure that we have credentials to send the message
If (!$O365Cred) {$O365Cred = Get-Credential}
# And that we’re connected to Exchange Online
Try { $OrgName = (Get-OrganizationConfig).Name }
Catch {
Write-Host “Your PowerShell session is not connected to Exchange Online.”
Write-Host “Please connect to Exchange Online using an administrative account and retry.”
Break }
It’s just a different way of attacking the problem. I wouldn’t connect to Exchange Online the way that you do. Instead, download the Exchange Online management module https://petri.com/exchange-online-powershell-module-v2 and use it instead. You can still get to the Search-UnifiedAuditLog cmdlet and you’re using modern authentication, etc.
Hello,
I have a question, you said: “After the period elapses, Stream removes the video and the content is irrecoverable.”
What of I didn’t wait until the 30 days elapsed, and I deleted the video from Recycle bin, would the stream admin in my company be able to restore it after I deleted it permanently from recycle bin before 30 days elapsed?
Thank you very much for answering.
Once the video is removed from the recycle bin, it is irrecoverable.