Microsoft 365 Install Prerequisites Task

Microsoft.SystemCenter.M365.Tasks.InstallPrerequisitesTask (Task)

Install prerequisites for the Microsoft 365 management pack watcher node.

Element properties:

TargetMicrosoft.SystemCenter.HealthService
AccessibilityPublic
CategoryCustom
EnabledTrue
RemotableFalse
Timeout300

Member Modules:

ID Module Type TypeId RunAs 
ExecuteScript WriteAction Microsoft.SystemCenter.M365.PowerShellWriteAction.Prerequisites Microsoft.SystemCenter.PrivilegedMonitoringAccount

Source Code:

<Task ID="Microsoft.SystemCenter.M365.Tasks.InstallPrerequisitesTask" Accessibility="Public" Enabled="true" Target="SC!Microsoft.SystemCenter.HealthService" Timeout="300" Remotable="true">
<Category>Custom</Category>
<WriteAction ID="ExecuteScript" TypeID="Microsoft.SystemCenter.M365.PowerShellWriteAction.Prerequisites" RunAs="SC!Microsoft.SystemCenter.PrivilegedMonitoringAccount">
<ScriptName>InstallPrerequisites.ps1</ScriptName>
<ScriptBody><Script>Param( [parameter(Mandatory=$false)] [string] $SkypeForBusinessNetworkAssessmentToolDesired = 'true' )

$api = new-object -comObject 'MOM.ScriptAPI'
$scriptName = 'InstallPrerequisites'
$eventId = 981
$step = 'At start.'
$api.LogScriptEvent($scriptName,$eventId,0,"Running script '$scriptName' (as '$(whoami)').")

#=========================================================================================
# Functions

function ConvertToBool([string] $value)
{
if (($value -eq $null) -or ($value -eq ''))
{
return $false
}
if ($value -like 'true')
{
return $true
}
return $false
}

function Is-CurrentUserMemberOfLocalAdministratorsGroup
{
$identityReferences = [Security.Principal.WindowsIdentity]::GetCurrent().Groups
foreach ($identityReference in $identityReferences)
{
if ($identityReference.Value -eq "S-1-5-32-544") #BuiltinAdministratorsSid
{
return $true
}
}
return $false
}

function Verify-PowerShell5
{
$step = 'Verifying PowerShell version'
if ($psVersionTable.PSVersion.Major -ge 5)
{
return 'AlreadyInstalled'
}
return 'NotInstalled'
}

function Verify-DotNetVersion
{
$step = 'Verifying .Net version'
$item = Get-ItemProperty 'HKLM:SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' -ErrorAction ignore
if (($item -ne $null) -and ($item.Release -ge 461808)) #4.7.2
{
return 'AlreadyInstalled'
}
return 'NotInstalled'
}

function Test-PathWithWait([string] $filePath,[int] $waitMaximumSeconds)
{
$count = 0
do {
Start-Sleep -s 1
$exists = Test-Path $filePath
if ($exists)
{
return $true
}
$count++
}
while ($count -lt $waitMaximumSeconds)
return $false
}

function Install-NetworkAssessmentTool
{
$step = 'Installing Microsoft Teams Network Assessment Tool'
$installedExePath = "${env:ProgramFiles(x86)}\Microsoft Teams Network Assessment Tool\NetworkAssessmentTool.exe"

$installed = Test-Path $installedExePath
if ($installed)
{
return 'AlreadyInstalled'
}

$createdFwRules = $false
$installerURL = "https://download.microsoft.com/download/e/0/2/e026d13b-9b1b-472a-b322-616c6e6d9c19/MicrosoftTeamsNetworkAssessmentTool.exe"
$downloaded = $false
$fileExistsAfterDownload = $false
$tempExePath = "$env:TEMP\MicrosoftTeamsNetworkAssessmentTool.exe"
try
{
$api.LogScriptEvent($scriptName,$eventId,0,$step)

$client = New-Object System.Net.WebClient

# This can throw an exception if the system is configured to disallow downloads - but, we're in a try/catch block that can already do the best it can.
$client.DownloadFile($installerURL,$tempExePath)
$downloaded = $true
# If an exception is NOT thrown but the file somehow still does not exist, then explicitly throw an exception
$fileExistsAfterDownload = Test-Path $tempExePath
if ($fileExistsAfterDownload -eq $false )
{
throw "File '$tempExePath' not found after successful download."
}

$description = "This tool provides the ability to perform a simple test of network performance and network connectivity to determine how well the network would perform for Microsoft Teams calls"

# Capture New-NetFirewallRule's output in a variable, or else it'll end up being part of the string returned - followed by the expected "Installed" or "NotInstalled".
# Also, only create the rules if they don't already exist - otherwise, the system is more than happy to create identical rules
# Make sure to use -ErrorAction Ignore, otherwise Get-NetFirewallRule throws an exception rather than returning an empty array if the entry is not found.
$rules = Get-NetFirewallRule -DisplayName 'Microsoft Teams Network Assessment Tool - TCP IN' -ErrorAction Ignore
if ( $rules -eq $null )
{
$res = New-NetFirewallRule -DisplayName 'Microsoft Teams Network Assessment Tool - TCP IN' -Description $description -Enabled True -Direction Inbound -Action Allow -Program $installedExePath -Protocol TCP -Profile @('Domain') -EdgeTraversalPolicy DeferToUser
}

$rules = Get-NetFirewallRule -DisplayName 'Microsoft Teams Network Assessment Tool - UDP IN' -ErrorAction Ignore
if ( $rules -eq $null )
{
$res = New-NetFirewallRule -DisplayName 'Microsoft Teams Network Assessment Tool - UDP IN' -Description $description -Enabled True -Direction Inbound -Action Allow -Program $installedExePath -Protocol UDP -Profile @('Domain') -EdgeTraversalPolicy DeferToUser
}

$rules = Get-NetFirewallRule -DisplayName 'Microsoft Teams Network Assessment Tool - TCP OUT' -ErrorAction Ignore
if ( $rules -eq $null )
{
$res = New-NetFirewallRule -DisplayName 'Microsoft Teams Network Assessment Tool - TCP OUT' -Description $description -Enabled True -Direction Outbound -Action Allow -Program $installedExePath -Protocol TCP -Profile @('Domain')
}

$rules = Get-NetFirewallRule -DisplayName 'Microsoft Teams Network Assessment Tool - UDP OUT' -ErrorAction Ignore
if ( $rules -eq $null )
{
$res = New-NetFirewallRule -DisplayName 'Microsoft Teams Network Assessment Tool - UDP OUT' -Description $description -Enabled True -Direction Outbound -Action Allow -Program $installedExePath -Protocol UDP -Profile @('Domain')
}
$createdFwRules = $true

&amp; "$tempExePath" /install /quiet
$installed = Test-PathWithWait $installedExePath 90
if ($installed)
{
return 'Installed'
}
$api.LogScriptEvent($scriptName,$eventId,1,"ERROR: $step failed. Target file does not exist following install. Check for other events (like security events) logged at this time.")
if ( $createdFwRules )
{
Remove-NetFirewallRule -DisplayName "Microsoft Teams Network Assessment Tool - *"
}
return 'NotInstalled'
}
catch
{
$msg = "ERROR: $step failed. Exception: $_"
if ( $downloaded -eq $false )
{
$msg += "`r`nUnable to download '$installerURL' to '$tempExePath'"
}
$api.LogScriptEvent($scriptName,$eventId,1,$msg)
if ( $createdFwRules )
{
Remove-NetFirewallRule -DisplayName "Microsoft Teams Network Assessment Tool - *"
}
return 'NotInstalled'
}
finally
{
Remove-Item $tempExePath -ErrorAction Ignore
}
}

#=========================================================================================
# Main

if (!(Is-CurrentUserMemberOfLocalAdministratorsGroup))
{
$output = "ERROR: Script run as user ('$(whoami)') who is not a member of the local Administrators group.
This task must be run as an account (i.e. 'SYSTEM') that is a member of the local Administrators group."
$api.LogScriptEvent($scriptName,$eventId,1,"Script finished. Returning: '$output'.")
$output
exit
}

$NetworkAssessmentToolDesired = ConvertToBool $SkypeForBusinessNetworkAssessmentToolDesired

try
{
# Make sure transport layer security protocols are at least TLS 1.2...
if ([Net.ServicePointManager]::SecurityProtocol -notlike '*Tls12*')
{
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
}

$verifyPowerShell5 = Verify-PowerShell5
$verifyDotNetVersion = Verify-DotNetVersion
if ($NetworkAssessmentToolDesired)
{
$installNetworkAssessmentTool = Install-NetworkAssessmentTool
}
}
catch
{
$api.LogScriptEvent($scriptName,$eventId,1,"ERROR: Unhandled exception at step '$step'. Exception: $_")
}

$output = "Prerequisite=PowerShell Version 5:$verifyPowerShell5;`r`n"
$output = $output + "Prerequisite=.Net Version 4.7.2 or greater:$verifyDotNetVersion;`r`n"
if ($NetworkAssessmentToolDesired)
{
$output = $output + "Prerequisite=Microsoft Teams Network Assessment Tool:$installNetworkAssessmentTool;`r`n"
}
$api.LogScriptEvent($scriptName,$eventId,0,"Script finished. Returning: '$output'.")
$output
</Script></ScriptBody>
<TimeoutSeconds>300</TimeoutSeconds>
<StrictErrorHandling>false</StrictErrorHandling>
<SkypeForBusinessNetworkAssessmentToolDesired>True</SkypeForBusinessNetworkAssessmentToolDesired>
</WriteAction>
</Task>