Issues with a Chrome Extension

I keep getting an RMM Alert on a daily basis at exactly 8 pm (machine’s local time ) regardless if the machine is used or just sitting idle :

“Type: Malware
Issues with a Chrome Extension”

And that’s it, there are no detailed report whatsoever. I scanned the device with Malwarebytes and it was clean, i also performed a clean reinstall of Chrome by deleting its cache and cleaning registry leftovers before doing a clean install. There are no 3rd party extensions used by the user except Grammarly and i don’t think it’s the one causing the alert. The user doesn’t seem to be effected by this at all.

The machine is a relatively weak desktop with 32-bit Win 7.

I’m starting to think it’s a bug with Syncro but i wanted to post here first to see if anyone had a similar issue.
Capture

Hi Ak, does this machine have BitDefender installed? If so it may be coming from there. What’s the asset name? If you’d like to PM it to me I can check it out.

1 Like

The machine doesn’t have BitDefender installed, i’ll pm you the asset name right away

Thank you! I don’t see anything in the logs that stand out. Two places to check would be the Webroot Console, and the Event Log- do you see any entries around the same timestamp as the alert?

1 Like

What you are seeing is that Chrome has Extensions that are getting flag. In this case, you need a script to remove them or at least reset the browser. The problem is you don’t know what user level is flagging the issue.

Untested but a nuke all option

Import-Module $env:SyncroModule
$users = Get-ChildItem c:\users | ?{ $_.PSIsContainer }

# When adding to the list, please mark what it is for...
$safe_list = @(
    "Temp", # Always Safe
    "pkedcjkdefgpdelpbcmbmeomcjbeemfm", # Chrome Media Router
    "bmnlcjabgnpnenekpadlanbbkooimhnj", # Honey
    "cfhdojbkjhnklbpkdaibdccddilifddb", # Ad Block Plus
    "gighmmpiobklfepjocnamgkkbiglidom", # Ad Block Plus
    "emhginjpijfggbofeediiojmdlmlkoik", # Avast Passwords
    "gomekmidlodglbbmalcneegieacbdmki", # Avast Online Security
    "eofcbnmajmjmplflapaojjnihcjkigck" # Avast Safe Price
)

foreach ( $user in $users ) {
    foreach ( $userpath in @( "C:\Users\$user\AppData\Local\Google\Chrome\User Data\Default\Extensions", "C:\Users\$user\AppData\Local\BraveSoftware\Brave-Browser\User Data\Default\Extensions" ) ) {
        $extensions = Get-ChildItem -Path $userpath -Directory | ?{ $_.name }
        foreach ( $extension in $extensions ) {
            if ( $extension -notin $safe_list ) {
                $to = "c:\temp\extensions\$user"
                New-Item -Path $to -ItemType Directory -Force | Out-Null
                Move-Item -Path "$userpath\$extension" -Destination $to -Force
                "$userpath\$extension"
            }
        }
    }
}

Or this single target: Run time: extension_name

Import-Module $env:SyncroModule
$users = Get-ChildItem c:\users | ?{ $_.PSIsContainer }

foreach ( $user in $users ) {
    foreach ( $userpath in @( "C:\Users\$user\AppData\Local\Google\Chrome\User Data\Default\Extensions", "C:\Users\$user\AppData\Local\BraveSoftware\Brave-Browser\User Data\Default\Extensions" ) ) {
        $extensions = Get-ChildItem -Path $userpath -Directory | ?{ $_.name }
        foreach ( $extension in $extensions ) {
            if ( $extension -eq $extension_name ) {
                $to = "c:\temp\extensions\$user"
                New-Item -Path $to -ItemType Directory -Force | Out-Null
                Move-Item -Path "$userpath\$extension" -Destination $to -Force
                "$userpath\$extension"
            }
        }
    }
}

While I can’t say mine is much better - I did improve a lot of the scripts at hand - if you like I can also post the script to finding the the problem extension.

2 Likes

Where do I set such an alert?
Is it part of the webroot integration?

This would be a script that is running across the devices alerting them that there is an issue. For the most part - you would set it up with your policies to be ran at X time or to be ran at the next time possible if the device is off line.

1 Like

I checked both Webroot Console and the Event Viewer and no logs with similar alert type around the same time. turned out it’s one of our scheduled scripts that triggers this alert, However i still don’t understand why it triggers on this device only - and there is no trace of suspicious malwares - since we use the script on all our clients machines.

Turned out it is indeed one of our scheduled scripts we got from the community and we’re using it in our policies across all assets, it’s “Find Malicious Chrome Extensions” and this is the log for the script on the target machine giving this alert.

error> The term 'ConvertFrom-Json' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is c
error> orrect and try again.
error> At C:\ProgramData\Syncro\bin\ac713221-fb04-4479-9bb3-56b0cbef9552.ps1:14 char:56
error> +         Get-Content $_.FullName -Raw | ConvertFrom-Json <<<<  | select @{n='ComputerName';e={$ComputerName}}, @{n='User';e={$Matches[1]}}, Name, Version, @{n='Path';e={$path}}
error>     + CategoryInfo          : ObjectNotFound: (ConvertFrom-Json:String) [], CommandNotFoundException
error>     + FullyQualifiedErrorId : CommandNotFoundException
error>  
error> The term 'ConvertFrom-Json' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is c
error> orrect and try again.
error> At C:\ProgramData\Syncro\bin\ac713221-fb04-4479-9bb3-56b0cbef9552.ps1:14 char:56
error> +         Get-Content $_.FullName -Raw | ConvertFrom-Json <<<<  | select @{n='ComputerName';e={$ComputerName}}, @{n='User';e={$Matches[1]}}, Name, Version, @{n='Path';e={$path}}
error>     + CategoryInfo          : ObjectNotFound: (ConvertFrom-Json:String) [], CommandNotFoundException
error>     + FullyQualifiedErrorId : CommandNotFoundException
error>  
error> The term 'ConvertFrom-Json' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is c
error> orrect and try again.
error> At C:\ProgramData\Syncro\bin\ac713221-fb04-4479-9bb3-56b0cbef9552.ps1:14 char:56
error> +         Get-Content $_.FullName -Raw | ConvertFrom-Json <<<<  | select @{n='ComputerName';e={$ComputerName}}, @{n='User';e={$Matches[1]}}, Name, Version, @{n='Path';e={$path}}
error>     + CategoryInfo          : ObjectNotFound: (ConvertFrom-Json:String) [], CommandNotFoundException
error>     + FullyQualifiedErrorId : CommandNotFoundException
error>  
error> The term 'ConvertFrom-Json' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is c
error> orrect and try again.
error> At C:\ProgramData\Syncro\bin\ac713221-fb04-4479-9bb3-56b0cbef9552.ps1:14 char:56
error> +         Get-Content $_.FullName -Raw | ConvertFrom-Json <<<<  | select @{n='ComputerName';e={$ComputerName}}, @{n='User';e={$Matches[1]}}, Name, Version, @{n='Path';e={$path}}
error>     + CategoryInfo          : ObjectNotFound: (ConvertFrom-Json:String) [], CommandNotFoundException
error>     + FullyQualifiedErrorId : CommandNotFoundException
error>  
error> The term 'ConvertFrom-Json' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is c
error> orrect and try again.
error> At C:\ProgramData\Syncro\bin\ac713221-fb04-4479-9bb3-56b0cbef9552.ps1:14 char:56
error> +         Get-Content $_.FullName -Raw | ConvertFrom-Json <<<<  | select @{n='ComputerName';e={$ComputerName}}, @{n='User';e={$Matches[1]}}, Name, Version, @{n='Path';e={$path}}
error>     + CategoryInfo          : ObjectNotFound: (ConvertFrom-Json:String) [], CommandNotFoundException
error>     + FullyQualifiedErrorId : CommandNotFoundException
error>  
error> The term 'ConvertFrom-Json' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is c
error> orrect and try again.
error> At C:\ProgramData\Syncro\bin\ac713221-fb04-4479-9bb3-56b0cbef9552.ps1:14 char:56
error> +         Get-Content $_.FullName -Raw | ConvertFrom-Json <<<<  | select @{n='ComputerName';e={$ComputerName}}, @{n='User';e={$Matches[1]}}, Name, Version, @{n='Path';e={$path}}
error>     + CategoryInfo          : ObjectNotFound: (ConvertFrom-Json:String) [], CommandNotFoundException
error>     + FullyQualifiedErrorId : CommandNotFoundException
error>  
error> The term 'ConvertFrom-Json' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is c
error> orrect and try again.
error> At C:\ProgramData\Syncro\bin\ac713221-fb04-4479-9bb3-56b0cbef9552.ps1:14 char:56
error> +         Get-Content $_.FullName -Raw | ConvertFrom-Json <<<<  | select @{n='ComputerName';e={$ComputerName}}, @{n='User';e={$Matches[1]}}, Name, Version, @{n='Path';e={$path}}
error>     + CategoryInfo          : ObjectNotFound: (ConvertFrom-Json:String) [], CommandNotFoundException
error>     + FullyQualifiedErrorId : CommandNotFoundException
error>  
error> The term 'ConvertFrom-Json' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is c
error> orrect and try again.
error> At C:\ProgramData\Syncro\bin\ac713221-fb04-4479-9bb3-56b0cbef9552.ps1:14 char:56
error> +         Get-Content $_.FullName -Raw | ConvertFrom-Json <<<<  | select @{n='ComputerName';e={$ComputerName}}, @{n='User';e={$Matches[1]}}, Name, Version, @{n='Path';e={$path}}
error>     + CategoryInfo          : ObjectNotFound: (ConvertFrom-Json:String) [], CommandNotFoundException
error>     + FullyQualifiedErrorId : CommandNotFoundException
error>  
error> The term 'ConvertFrom-Json' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is c
error> orrect and try again.
error> At C:\ProgramData\Syncro\bin\ac713221-fb04-4479-9bb3-56b0cbef9552.ps1:14 char:56
error> +         Get-Content $_.FullName -Raw | ConvertFrom-Json <<<<  | select @{n='ComputerName';e={$ComputerName}}, @{n='User';e={$Matches[1]}}, Name, Version, @{n='Path';e={$path}}
error>     + CategoryInfo          : ObjectNotFound: (ConvertFrom-Json:String) [], CommandNotFoundException
error>     + FullyQualifiedErrorId : CommandNotFoundException
error>  
Found Malicious Plugin
Call-KabutoApi: success
Timed out (00:10:00).

Yup, normally when I see error like the one you are providing it means there is an issue with their PowerShell. I personally haven’t look into the issue yet to see what might be the fix, but I am going to awesome if you look at other scripts running for that device they are ending in failure as well.

Keep in mind - not everything is an attack - it could simply be a PUP instead. “Find Malicious Chrome Extensions” will say there is “malware” when the extension is just maybe not wanted. Like Price Finder.

1 Like

Indeed you’re right, all of the scheduled scripts for this machine specifically fails to run, could it be the fact that this machine runs on Win 7 and the scripts are not compatible with it? This is the only machine with Win 7 we have.

Yes and no. While there will be always commands that are not going to work across all Windows Platforms, I have seen this issue across 7 up to 11. Even after upgrading PS to 5.1. I think it has something to do with permissions of running PowerShell on the machine it self. Again, I havent really look too hard into the issue yet as we only have maybe like 4 machines display this issue.

1 Like

Interesting, I’ll do more research on the issue then but for now this clears up the confusion i had. Thank you very much i really appreciate your help.

Yea no problem! Funny enough we just onboarded a new client that has a computer display the issue again, so I will be looking into the issue over the next week. If I come across a solution that works I will let you know.

1 Like

Oh wow haha! yeah if you end up figuring out a fix this will be really helpful! Thanks again.

You need to update PowerShell. If you use chocolatey, you can just run

choco upgrade powershell -y -f

It will take a while to run, probably a couple of hours. Syncro requires PS 4, Windows 7 is probably 2 or under. You can use $PSVersionTable to show the PS version to confirm.

1 Like

Worked perfectly! updated PS, executed the Chrome script again and it ran successfully and no alert showed up!

Thank you !

This solved the issue for me. Hope it helps you aswell.

Glad you was able to get it resolved:) Unfortunately as I said before - updating to 5.1 should fix the issue, but I do have a few that simply do not wish to work:) I was able to figure out the issue though.

For others that need it:

Import-Module $env:SyncroModule


$version = "5.1"
$username = ""
$password = ""
$verbose = $true

$ErrorActionPreference = 'Stop'
if ($verbose) {
    $VerbosePreference = "Continue"
}

$tmp_dir = $env:temp
if (-not (Test-Path -Path $tmp_dir)) {
    New-Item -Path $tmp_dir -ItemType Directory > $null
}

Function Write-Log($message, $level="INFO") {
    # Poor man's implementation of Log4Net
    $date_stamp = Get-Date -Format s
    $log_entry = "$date_stamp - $level - $message"
    $log_file = "$tmp_dir\upgrade_powershell.log"
    Write-Verbose -Message $log_entry
    Log-Activity -Message $log_entry
    Add-Content -Path $log_file -Value $log_entry
}

Function Reboot-AndResume {
    Write-Log -message "Adding script to run on next logon"
    $script_path = $script:MyInvocation.MyCommand.Path
    $ps_path = "$env:SystemDrive\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
    $arguments = "-version $version"
    if ($username -and $password) {
        $arguments = "$arguments -username `"$username`" -password `"$password`""
    }
    if ($verbose) {
        $arguments = "$arguments -Verbose"
    }

    $command = "$ps_path -ExecutionPolicy ByPass -File $script_path $arguments"
    $reg_key = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce"
    $reg_property_name = "ps-upgrade"
    Set-ItemProperty -Path $reg_key -Name $reg_property_name -Value $command

    if ($username -and $password) {
        $reg_winlogon_path = "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon"
        Set-ItemProperty -Path $reg_winlogon_path -Name AutoAdminLogon -Value 1
        Set-ItemProperty -Path $reg_winlogon_path -Name DefaultUserName -Value $username
        Set-ItemProperty -Path $reg_winlogon_path -Name DefaultPassword -Value $password
        Write-Log -message "Rebooting server to continue powershell upgrade"
    } else {
        Write-Log -message "Need to reboot and log back in to continue powershell upgrade"
        Rmm-Alert -Category 'powershell_out_of_date' -Body "Need to reboot and log back in to continue powershell upgrade"

        $error_msg = "The script will restart on the next login automatically"
        Write-Log -message $error_msg -level "ERROR"
        Restart-Computer -Force
        #throw $error_msg

    }

    if (Get-Command -Name Restart-Computer -ErrorAction SilentlyContinue) {
        Restart-Computer -Force
    } else {
        # PS v1 (Server 2008) doesn't have the cmdlet Restart-Computer, use el-traditional
        shutdown /r /t 0
    }
}

Function Run-Process($executable, $arguments) {
    $process = New-Object -TypeName System.Diagnostics.Process
    $psi = $process.StartInfo
    $psi.FileName = $executable
    $psi.Arguments = $arguments
    Write-Log -message "Starting new process '$executable $arguments'"
    $process.Start() | Out-Null
    
    $process.WaitForExit() | Out-Null
    $exit_code = $process.ExitCode
    Write-Log -message "Process completed with exit code '$exit_code'"

    return $exit_code
}

Function Download-File($url, $path) {
    Write-Log -message "Downloading url '$url' to '$path'"
    $client = New-Object -TypeName System.Net.WebClient
    $client.DownloadFile($url, $path)
}

Function Clear-AutoLogon {
    $reg_winlogon_path = "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon"
    Write-Log -message "Clearing auto logon registry properties"
    Set-ItemProperty -Path $reg_winlogon_path -Name AutoAdminLogon -Value 0
    Remove-ItemProperty -Path $reg_winlogon_path -Name DefaultUserName -ErrorAction SilentlyContinue
    Remove-ItemProperty -Path $reg_winlogon_path -Name DefaultPassword -ErrorAction SilentlyContinue
}

Function Download-Wmf5Server2008($architecture) {
    if ($architecture -eq "x64") {
        $zip_url = "http://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/Win7AndW2K8R2-KB3191566-x64.zip"
        $file = "$tmp_dir\Win7AndW2K8R2-KB3191566-x64.msu"
    } else {
        $zip_url = "http://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/Win7-KB3191566-x86.zip"
        $file = "$tmp_dir\Win7-KB3191566-x86.msu"
    }
    if (Test-Path -Path $file) {
        return $file
    }

    $filename = $zip_url.Split("/")[-1]
    $zip_file = "$tmp_dir\$filename"
    Download-File -url $zip_url -path $zip_file

    Write-Log -message "Extracting '$zip_file' to '$tmp_dir'"
    try {
        Add-Type -AssemblyName System.IO.Compression.FileSystem > $null
        $legacy = $false
    } catch {
        $legacy = $true
    }

    if ($legacy) {
        $shell = New-Object -ComObject Shell.Application
        $zip_src = $shell.NameSpace($zip_file)
        $zip_dest = $shell.NameSpace($tmp_dir)
        $zip_dest.CopyHere($zip_src.Items(), 1044)
    } else {
        [System.IO.Compression.ZipFile]::ExtractToDirectory($zip_file, $tmp_dir)
    }

    return $file
}

Write-Log -message "Starting script"
$PSVersionTable.PSVersion.Major

# on PS v1.0, upgrade to 2.0 and then run the script again
if ($PSVersionTable -eq $null) {
    Write-Log -message "Upgrading powershell v1.0 to v2.0"
    $architecture = $env:PROCESSOR_ARCHITECTURE
    if ($architecture -eq "AMD64") {
        $url = "https://download.microsoft.com/download/2/8/6/28686477-3242-4E96-9009-30B16BED89AF/Windows6.0-KB968930-x64.msu"
    } else {
        $url = "https://download.microsoft.com/download/F/9/E/F9EF6ACB-2BA8-4845-9C10-85FC4A69B207/Windows6.0-KB968930-x86.msu"
    }
    $filename = $url.Split("/")[-1]
    $file = "$tmp_dir\$filename"
    Download-File -url $url -path $file
    $exit_code = Run-Process -executable $file -arguments "/quiet /norestart"
    if ($exit_code -ne 0 -and $exit_code -ne 3010) {
        $error_msg = "failed to update Powershell from 1.0 to 2.0: exit code $exit_code"
        Write-Log -message $error_msg -level "ERROR"
        throw $error_msg
    }
    Reboot-AndResume
}

# exit if the target version is the same as the actual version or actual version is greater than target version
$current_ps_version = [version]"$($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor)"
if ($current_ps_version -eq [version]$version) {
    Write-Log -message "Current and target PS version are the same ($($current_ps_version)), no action is required"
    Clear-AutoLogon
    exit 0
}

if ($([int][string]$current_ps_version) -gt $([int]$version)) {
    Write-Log -message "Current version $($current_ps_version) is higher than target PS version $([version]$version), no action is required"
    Clear-AutoLogon
    exit 0
}

$os_version = [Version](Get-Item -Path "$env:SystemRoot\System32\kernel32.dll").VersionInfo.ProductVersion
$architecture = $env:PROCESSOR_ARCHITECTURE
if ($architecture -eq "AMD64") {
    $architecture = "x64"
} else {
    $architecture = "x86"
}

$actions = @()
switch ($version) {
    "3.0" {
        $actions += "3.0"
        break
    }
    "4.0" {
        if ($os_version -lt [version]"6.1") {
            $error_msg = "cannot upgrade Server 2008 to Powershell v4, v3 is the latest supported"
            Write-Log -message $error_msg -level "ERROR"
            throw $error_msg
        }
        $actions += "4.0"
        break
    }
    "5.1" {
        if ($os_version -lt [version]"6.1") {
            $error_msg = "cannot upgrade Server 2008 to Powershell v5.1, v3 is the latest supported"
            Write-Log -message $error_msg -level "ERROR"
            throw $error_msg
        }
        # check if WMF 3 is installed, need to be uninstalled before 5.1
        if ($os_version.Minor -lt 2) {
            $wmf3_installed = Get-Hotfix -Id "KB2506143" -ErrorAction SilentlyContinue
            if ($wmf3_installed) {
                $actions += "remove-3.0"
            }
        }
        $actions += "5.1"
        break
    }
    default {
        $error_msg = "version '$version' is not supported in this upgrade script"
        Write-Log -message $error_msg -level "ERROR"
        throw $error_msg
    }
}

# detect if .NET 4.5.2 is not installed and add to the actions
$dotnet_path = "HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full"
if (-not (Test-Path -Path $dotnet_path)) {
    $dotnet_upgrade_needed = $true
} else {
    $dotnet_version = Get-ItemProperty -Path $dotnet_path -Name Release -ErrorAction SilentlyContinue
    if ($dotnet_version) {
        # 379893 == 4.5.2
        if ($dotnet_version.Release -lt 379893) {
            $dotnet_upgrade_needed = $true
        }        
    } else {
        $dotnet_upgrade_needed = $true
    }
}
if ($dotnet_upgrade_needed) {
    $actions = @("dotnet") + $actions
}

Write-Log -message "The following actions will be performed: $($actions -join ", ")"
foreach ($action in $actions) {
    $url = $null
    $file = $null
    $arguments = "/quiet /norestart"

    switch ($action) {
        "dotnet" {
            Write-Log -message "Running .NET update to 4.5.2"
            $url = "https://download.microsoft.com/download/E/2/1/E21644B5-2DF2-47C2-91BD-63C560427900/NDP452-KB2901907-x86-x64-AllOS-ENU.exe"
            $error_msg = "failed to update .NET to 4.5.2"
            $arguments = "/q /norestart"
            break
        }
        "remove-3.0" {
            # this is only run before a 5.1 install on Windows 7/2008 R2, the
            # install zip needs to be downloaded and extracted before
            # removing 3.0 as then the FileSystem assembly cannot be loaded
            Write-Log -message "Downloading WMF/PS v5.1 and removing WMF/PS v3 before version 5.1 install"
            Download-Wmf5Server2008 -architecture $architecture > $null

            $file = "wusa.exe"
            $arguments = "/uninstall /KB:2506143 /quiet /norestart"
            break
        }
        "3.0" {
            Write-Log -message "Running powershell update to version 3"
            if ($os_version.Minor -eq 1) {
                $url = "https://download.microsoft.com/download/E/7/6/E76850B8-DA6E-4FF5-8CCE-A24FC513FD16/Windows6.1-KB2506143-$($architecture).msu"
            } else {
                $url = "https://download.microsoft.com/download/E/7/6/E76850B8-DA6E-4FF5-8CCE-A24FC513FD16/Windows6.0-KB2506146-$($architecture).msu"
            }
            $error_msg = "failed to update Powershell to version 3"
            break
        }
        "4.0" {
            Write-Log -message "Running powershell update to version 4"
            if ($os_version.Minor -eq 1) {
                $url = "https://download.microsoft.com/download/3/D/6/3D61D262-8549-4769-A660-230B67E15B25/Windows6.1-KB2819745-$($architecture)-MultiPkg.msu"
            } else {
                $url = "https://download.microsoft.com/download/3/D/6/3D61D262-8549-4769-A660-230B67E15B25/Windows8-RT-KB2799888-x64.msu"
            }
            $error_msg = "failed to update Powershell to version 4"
            break
        }
        "5.1" {
            Write-Log -message "Running powershell update to version 5.1"
            if ($os_version.Minor -eq 1) {
                # Server 2008 R2 and Windows 7, already downloaded in remove-3.0
                $file = Download-Wmf5Server2008 -architecture $architecture
            } elseif ($os_version.Minor -eq 2) {
                # Server 2012
                $url = "http://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu"
            } else {
                # Server 2012 R2 and Windows 8.1
                if ($architecture -eq "x64") {
                    $url = "http://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/Win8.1AndW2K12R2-KB3191564-x64.msu"
                } else {
                    $url = "http://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/Win8.1-KB3191564-x86.msu"
                }
            }
            break
        }
        default {
            $error_msg = "unknown action '$action'"
            Write-Log -message $error_msg -level "ERROR"
            Write-Log -message "Reboot the target machine to ensure the previous install attempt did not just need a reboot"
        }
    }

    if ($file -eq $null) {
        $filename = $url.Split("/")[-1]
        $file = "$tmp_dir\$filename"
    }
    if ($url -ne $null) {
        Download-File -url $url -path $file
    }
    
    $exit_code = Run-Process -executable $file -arguments $arguments
    if ($exit_code -ne 0 -and $exit_code -ne 3010) {
        $log_msg = "$($error_msg): exit code $exit_code"
        Write-Log -message $log_msg -level "ERROR"
        throw $log_msg
    }
    if ($exit_code -eq 3010) {
        Reboot-AndResume
        break
    }
}