1+ <#
2+ .COPYRIGHT
3+ Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
4+ See LICENSE in the project root for license information.
5+ #>
6+
7+ # Remote Desktop Multimedia Redirection updater - Remediation Script
8+ # Version 0.0.1
9+
10+ # ####################################
11+
12+ Param (
13+ [parameter (mandatory = $false , HelpMessage = " Log path and file name" )]
14+ [string ]$logpath = " $env: windir \temp\TeamsWebRTC-remediate.log" ,
15+ [parameter (mandatory = $false , HelpMessage = " time to wait past disconnect" )]
16+ [string ]$DCNTwait = 600 ,
17+ [parameter (mandatory = $false , HelpMessage = " time to wait to re-check user state" )]
18+ [string ]$StateDetWait = 300 ,
19+ [parameter (mandatory = $false , HelpMessage = " evaluate user state only once" )]
20+ [switch ]$retry ,
21+ [parameter (mandatory = $false , HelpMessage = " time in minutes to timeout" )]
22+ [int ]$TimeOut = 60
23+ )
24+
25+ # function to handle logging
26+ function update-log {
27+ Param (
28+ [Parameter (
29+ Mandatory = $true ,
30+ ValueFromPipeline = $true ,
31+ ValueFromPipelineByPropertyName = $true ,
32+ Position = 0
33+ )]
34+ [string ]$Data ,
35+ [validateset (' Information' , ' Warning' , ' Error' , ' Comment' )]
36+ [string ]$Class = " Information" ,
37+ [validateset (' Console' , ' File' , ' Both' )]
38+ [string ]$Output
39+ )
40+
41+ $date = get-date - UFormat " %m/%d/%y %r"
42+ $String = $Class + " " + $date + " " + $data
43+ if ($Output -eq " Console" ) { Write-Output $string | out-host }
44+ if ($Output -eq " file" ) { Write-Output $String | out-file - FilePath $logpath - Append }
45+ if ($Output -eq " Both" ) {
46+ Write-Output $string | out-host
47+ Write-Output $String | out-file - FilePath $logpath - Append
48+ }
49+ }
50+
51+ # function to query the user state and convert to variable
52+ function get-userstate {
53+ (((quser) -replace ' ^>' , ' ' ) -replace ' \s{2,}' , ' ,' ).Trim() | ForEach-Object {
54+ if ($_.Split (' ,' ).Count -eq 5 ) {
55+ Write-Output ($_ -replace ' (^[^,]+)' , ' $1,' )
56+ }
57+ else {
58+ Write-Output $_
59+ }
60+ } | ConvertFrom-Csv
61+ }
62+
63+ # function to perform the upgrading
64+ function invoke-remediation {
65+
66+ $MMRCurrent = get-CurrentMMRver
67+
68+ $MMRInstalled = get-installedMMRver
69+ $string = " Installed MMR agent version is " + $MMRInstalled
70+ update-log - Data $string - Class Information - Output Both
71+ $string = " Latest version of MMR agent is " + $MMRCurrent
72+ update-log - Data $string - Class Information - Output Both
73+
74+ try {
75+
76+ # Create a directory to save download files
77+ $tempCreated = $false
78+ if (! (Test-Path C:\RDMMRtemp)) {
79+ New-Item - Path C:\ - ItemType Directory - Name RDMMRtemp | Out-Null
80+ update-log - data " Temp path created" - output both - Class Information
81+ $tempCreated = $true
82+ }
83+
84+ # Download MMR
85+ update-log - Data " Downloading RD MMR client" - Class Information - output both
86+ invoke-WebRequest - Uri " https://aka.ms/avdmmr/msi" - OutFile " C:\RDMMRtemp\MMR_Installer.msi" - UseBasicParsing - PassThru
87+
88+ # Install MMR
89+ update-log - Data " Installing RD MMR client" - Class Information - output both
90+ $msireturn = Start-Process msiexec.exe - ArgumentList ' /i C:\RDMMRtemp\MMR_Installer.msi /q /n /l*voicewarmup c:\windows\temp\RDMMRmsi.log' - Wait - PassThru
91+ if ($msireturn.ExitCode -eq ' 0' ) {
92+ update-log - data " MSIEXEC returned 0" - Class Information - Output Both
93+ }
94+ else {
95+ $string = " MSIEXEC returned exit code " + $msireturn.ExitCode
96+ update-log - data $string - Class Information - Output Both
97+ exit 1
98+ }
99+
100+ if ($tempCreated -eq $true ) {
101+ # Remove temp folder
102+ update-log - Data " Removing temp directory" - Class Information - output both
103+ Remove-Item - Path C:\RDMMRtemp\ - Recurse | out-null
104+ }
105+ else {
106+ # Remove downloaded WebRTC file
107+ update-log - Data " Removing RD MMR client installer file" - Class Information - output both
108+ Remove-Item - Path C:\RDMMRtemp\MsRdcWebRTCSvc_HostSetup.msi
109+ }
110+ # Return Success
111+ update-log - Data " Media Optimization Installed" - Class Information - output both
112+ $MMRCurrent = get-CurrentMMRver
113+ $string = " Current installed version is now " + $MMRCurrent
114+ update-log - Data $string - Class Information - Output Both
115+ return " Success"
116+ }
117+ catch {
118+ Write-Error - ErrorRecord $_
119+ return / b " Fail"
120+ }
121+ }
122+
123+ # function to handle user state detection logic
124+ function invoke-userdetect {
125+ update-log - data " Detecting user state." - Class Information - output both
126+ $explorerprocesses = @ (Get-WmiObject - Query " Select * FROM Win32_Process WHERE Name='explorer.exe'" - ErrorAction SilentlyContinue)
127+ if ($explorerprocesses.Count -eq 0 ) {
128+ update-log - data " There is not a user logged in. Skipping user state detection." - Class Information - Output both
129+ Return
130+ }
131+ else {
132+ foreach ($i in $explorerprocesses ) {
133+ $Username = $i.GetOwner ().User
134+ $Domain = $i.GetOwner ().Domain
135+ $string = $Domain + " \" + $Username + " logged on since: " + ($i.ConvertToDateTime ($i.CreationDate ))
136+ update-log - data $string - Class Information - Output Both
137+ }
138+ update-log - data " There is a logged on user" - Class Information - Output Both
139+ }
140+
141+ if ($retry -eq $true ) {
142+ do {
143+ $session = get-userstate
144+ $text = " Waiting for non-active user state."
145+ update-log - data $text - Class Information - output both
146+ $String = " Session State is " + $session.STATE
147+ update-log - data $String - output both - Class Information
148+ $string = " Idle Time is " + $session .' IDLE TIME'
149+ update-log - data $String - output both - Class Information
150+
151+ if ($TimeOut -gt 0 ) {
152+ sleep - Seconds $StateDetWait
153+ $TimeOut = ($TimeOut - $StateDetWait )
154+ }
155+ else {
156+ update-log - Data " Timed out. Returning fail" - Class Error - output both
157+ return 3
158+ }
159+ } while ($session.state -eq " Active" )
160+
161+ update-log - data " User state is not active." - output both - Class Information
162+ invoke-disctimer
163+ }
164+
165+ if ($retry -eq $false ) {
166+ update-log - Data " Attempting to detect only once." - Class Information - output both
167+ $session = get-userstate
168+ if ($session.state -eq " disc" ) {
169+ $text = " User state is disconnected"
170+ update-log - data $text - Class Information - output both
171+ }
172+ else {
173+ update-log - Data " User state is not disconnected" - Class Warning - output both
174+ return 2
175+ }
176+ }
177+ }
178+
179+ # function to handle wait time between first non-active discovery and upgrade
180+ function invoke-disctimer {
181+ $string = " Waiting " + $DCNTwait + " seconds..."
182+ update-log - Data $string - Class Information - output both
183+ sleep - Seconds $DCNTwait
184+ $Timeout = ($Timeout - $DCNTwait )
185+
186+ $session = get-userstate
187+ if ($session.STATE -eq " Active" ) {
188+ update-log - Data " User state has become active again. Waiting for non-active state..." - Class Warning - output both
189+ invoke-userdetect
190+ }
191+ else {
192+ update-log - data " Session state is still non-active. Continuing with remediation..." - Class Information - output both
193+ }
194+ }
195+
196+ # function to query the latest available version number of RD MMR client
197+ function get-CurrentMMRver {
198+ $response = (Invoke-WebRequest - Uri " https://aka.ms/avdmmr/msi" - UseBasicParsing)
199+ $versionC = $response.BaseResponse.ResponseUri.AbsolutePath -replace " .*HostInstaller_" , " " -replace " .x64.msi*" , " "
200+ $string = " The latest available version of the RD MMR client is " + $versionC
201+ update-log - Data $string - Class Information - output both
202+ return $versionC
203+ }
204+
205+ # function to determine what version of RDMMR is installed
206+ function get-installedMMRver {
207+ if ((Test-Path - Path ' C:\Program Files\MsRDCMMRHost\MsMmrHost.exe' ) -eq $true ){
208+ $version = (Get-ItemProperty - Path ' C:\Program Files\MsRDCMMRHost\MsMmrHost.exe' )
209+ $string = " The currently installed version of the RD MMR client is " + $version.VersionInfo.ProductVersion
210+ update-log - Data $string - Class Information - output both
211+ return $version.VersionInfo.ProductVersion
212+ }
213+ else
214+ {
215+ update-log - data " It doesn't appear that the RD MMR client is installed" - Class Warning - output both
216+ return " 0" }
217+ }
218+
219+ # Opening text of log.
220+ update-log - Data " " - Class Information - output both
221+ update-log - Data " *** Starting RD MMR agent remediation ***" - Class Information - output both
222+ update-log - Data " " - Class Information - output both
223+
224+ # Display timeout amount in the log - if using retry function
225+ if ($retry -eq $true ) {
226+ $String = " Time out set for " + $Timeout + " minutes"
227+ update-log - Data $String - output both - Class Information
228+ }
229+
230+ # Converts Timeout minutes to seconds
231+ $TimeOut = $TimeOut * 60
232+
233+ # Starts the user state detection and handling
234+ $var1 = invoke-userdetect
235+
236+ # Exit if user is active (default).
237+ # Return code for no retry - user is active
238+ if ($var1 -eq 2 ) {
239+ update-log - Data " User State is active. Returning fail. Try again" - Class Warning - output both
240+ exit 1
241+ }
242+
243+ # Exit if process times out. Used with "-retry" parameter.
244+ # Return code for time out
245+ if ($var1 -eq 3 ) {
246+ update-log - Data " Timed out. Returning fail. Try again" - Class Warning - output both
247+ exit 1
248+ }
249+
250+ # Starts the remediaiton function
251+ $result = $null
252+ $result = invoke-remediation
253+
254+ # Exit if the remediation was successful
255+ if ($result -eq " Success" ) {
256+ update-log - Data " Remediation Complete" - Class Information - output both
257+ exit 0
258+ }
259+
260+ # Exit if remediation failed
261+ if ($result -ne " Success" ) {
262+ update-log - Data " An error occured." - Class Error - output both
263+ exit 1
264+ }
265+
0 commit comments