Example Powershell Script for GPO Deployment of BCS Suite for Windows

Example Powershell Script for GPO Deployment of BCS Suite for Windows

This sample Powershell script is ONLY an example, which you can follow as a template for your GPO deployment of the Barracuda Content Shield Suite for Windows. This script is not guaranteed by Barracuda, and you must replace the parameter values with your own. Note that you can change the filenames of the powershell script (ExampleConfigBCS.ps1) and/or batch file (ConfigureBCSPlus.bat), but make sure to update the batch file with the revised powershell script name, and that the GPO procedure references the new file names. param( [Parameter(Mandatory=$true,HelpMessage='Specify "install" or "remove"')] [string]$action='', [Parameter(Mandatory=$true,HelpMessage='Specify working directory. Gets created if does not exist and setup files are copied here.')] [string]$workDir='', [Parameter(HelpMessage='Required for "install" action only. BCS setup executable file name')] [string]$setupName='', [Parameter(HelpMessage='Required for "install" action only. BCS account configuration (*.key) file name')] [string]$keyName='', [Parameter(HelpMessage='Required for customized "install" only. Set to "WebFiltering" or "MalwarePrevention". Not setting this defaults to install both.')] [string]$feature='', [Parameter(HelpMessage='Optional. Pass version, i.e. "2.0.0.0", if original installer filename is renamed.')] [string]$version='', [Parameter(HelpMessage='Optional. Pass this flag and set to "1" if logging to Event log Application.evtx')] [string]$logEventLog='', [Parameter(HelpMessage='User password for uninstall (obsolete, since tamperproof gets disabled in this script)')] [string]$userPass='' ) #Must be the first statement in the script #### CUSTOM SETUP #################################################################### <# =NOTES ============================================================================= -- TESTED WITH WIN10. Needs adjustments for running on WIN7 (Powershell < 3.0), i.e. "Tee-Object" -- Make sure to adjust the parameter contents for install/upgrade or uninstall to your setup Deploy via GPO: Create a per-machine GPO that executes a Startup Script (make sure it runs elevated) that invokes the powershell script - or any other script that you created to install / remove the agent suite installer (executable) Return codes: # exitcode 3010 currently returned as 0. Remove condition from ExitWithCode function in order to receive this return code. # 3010 - Uninstall successful. Reboot pending: On uninstall a reboot required before reinstalling the agent. 0 - Installation successful. (Also on a "remove" action, if the agent is not installed):: 1601 - Install aborted. SETUP.EXE not found. 1601 - Install aborted. SETUP.EXE not found. 1602 - Install aborted. Check KEYPATH value. 1603 - Uninstall canceled. Most likely because either tamper proof is disabled or wrong password was provided For any of the 16xx return codes, please check the component MSI logs that can be found in the %temp% folder of the process owner ==================================================================================== #> # ACTION: set the action to do in this script:( install | remove ) $ACTION=$action # BASE_DIR: set your network share folder as base dir and some other values $BASE_DIR =$workDir # SETUP_FILENAME: by default named BarracudaContentShieldSetup-[VERSION].exe. If you rename this field, make sure to also fill in the VERSION field below. $SETUP_FILENAME =$setupName # KEY_FILENAME: by default named bcs.key. Change this property if you plan to rename this key (but make sure to keep ".key" as the extension) $KEY_FILENAME =$keyName #VERSION: no need to set this if you plan to keep the filename original (which contains the version). In that case the following lines extract the version from the file name $VERSION =$version #FEATURE: only set this to override default complete install - to install ONLY EITHER WebFiltering OR Malware Protection - possible values: "WebFiltering" | "MalwarePrevention" $FEATURE =$feature # creates an operational log file in workDir if logEventLog not set $LOG_FILE =$(Join-Path $workDir 'install.log') #### SETUP DONE #################################################################### if($VERSION -eq "" -and $action -ne "remove"){ #try to get it from our setup name. Of course this can only work if the name is not modified $VERSION =($SETUP_FILENAME).split("-""")[1] $VERSION = $VERSION.Substring(0,$VERSION.Length-4) # TODO: some regex to validate the result is in fact a version number } $actions = @("install","remove") $ErrorActionPreference="Stop" $level=@{INFO="[INFO]";WARNING="[WARN]";ERROR="[ERROR]"} $date=(Get-Date).ToString('MMddyy_HHmmss') $MMddyyyy=(Get-Date).ToString('MM-dd-yyyy'); # creates an operational event source for Application.evtx If($logEventLog) { $eventSource = "$($MyInvocation.MyCommand.Name)" if($eventSource -eq "") { $eventSource = "ConfigureBCS.ps1"} if(-not([System.Diagnostics.EventLog]::SourceExists("$eventSource"))){ New-EventLog -LogName Application -Source "$eventSource" } } function Bcs-Log { Param( [Parameter(Mandatory=$true)] [string]$LogMessage, [Parameter(Mandatory=$true)] [string]$eventType ) If($logEventLog) { Try{ Write-EventLog -LogName Application -Source "$eventSource" -EntryType $eventType -EventId 1 -Message $LogMessage } Catch{ $ErrorMessage = $_.Exception.Message Write-Host "$LogMessage, Error: $ErrorMessage" } } Else { Write-Output "$(Get-Date) : [$($eventType)] $($LogMessage)" | Tee-Object -FilePath $LOG_FILE -Append } } # make sure workDir exists mkdir $BASE_DIR -ErrorAction SilentlyContinue # Starting script execution Bcs-Log -LogMessage "*** START $action *************************************************`nParams: ACTION=$action, WORKDIR=$workDir, SETUP_NAME=$setupName, KEY_NAME=$keyName, VERSION=$version, FEATURE=$feature" -eventType Information $bcs_suite_installed='' $bcs_cpa_installed='' $bcs_wca_installed='' function ExitWithCode { param ( [Int32]$exitcode ) if($exitcode-eq 3010){ # we expect exitcode 3010, as this just states pending reboot. # you can check the component logs in %temp% folder (MSI*.log) for further details on the installation of each component. Bcs-Log -LogMessage "Requested action '$ACTION' finished with return code: $exitcode. The action was successful. A Reboot is pending." -eventType Information exit 0 #handle as success } else{ Bcs-Log -LogMessage "Action '$ACTION' finished with return code: $exitcode" -eventType Information }   $host.SetShouldExit($exitcode) exit $exitcode } function InstallOrUpgradeBCSPlus { <# .SYNOPSIS Installs or upgrades BCS Suite using given parameters. .DESCRIPTION Depending on state this will either install or upgrade BCS Suite if installer detects a lower version of the BCS Suite is already installed. The command is the same. Once installer finishes this script exits. #> $bcsExePath = $(Join-Path $BASE_DIR $SETUP_FILENAME) $bcsKeyPath = $(Join-Path $BASE_DIR $KEY_FILENAME) $installShieldLogPath = $(Join-Path $BASE_DIR InstallShield.log) # copy setup files to workDir if($PSScriptRoot -ne $BASE_DIR){ $msg = "$PSScriptRoot != $BASE_DIR" # copy over files from script dir. If none found here, assume setup files are already in specified workDir if(Test-Path "$(Join-Path $PSScriptRoot $SETUP_FILENAME)"){ $msg += "`nCopy Setup Files to $($BASE_DIR): $SETUP_FILENAME and $KEY_FILENAME" Copy-Item -Path "$(Join-Path $PSScriptRoot $SETUP_FILENAME)" -Destination $bcsExePath -Recurse -Force Copy-Item -Path "$(Join-Path $PSScriptRoot $KEY_FILENAME)" -Destination $bcsKeyPath -Recurse -Force } Bcs-Log -LogMessage $msg -eventType Information } if(-not (Test-Path $bcsExePath)) { Bcs-Log -LogMessage "$($bcsExePath) does not exist" -eventType Error ExitWithCode -exitcode 1601 } if(-not (Test-Path $bcsKeyPath)) { Bcs-Log -LogMessage "$($bcsKeyPath) does not exist" -eventType Error ExitWithCode -exitcode 1602 } [string[]]$arguments = @() If ($KEY_FILENAME -ne ""){ $arguments += "KEYPATH=`"$($bcsKeyPath)`"" } If ($feature -ne ""){ $arguments += "ISFeatureInstall=$($($feature -split "," | foreach({"$($_.Trim())"})) -join ',')" } Bcs-Log -LogMessage "Install: $bcsExePath $arguments /debuglog`"$($installShieldLogPath)`" /silent" -eventType Information $process = (Start-Process -FilePath "$($bcsExePath)" -ArgumentList "$arguments /debuglog`"$($installShieldLogPath)`" /silent" -PassThru -Wait -NoNewWindow) ExitWithCode -exitcode $process.ExitCode } function Remove-Pass { <# .SYNOPSIS Remove tamper proof protection from installation. .DESCRIPTION In order to be able to uninstall the tamper proof protection needs to be disabled. This function removes the protection so that uninstall command does not require the password to be checked against. #> Bcs-Log -LogMessage "Try stopping BCS Services if installed.." -eventType Information Stop-Service -Name "bcs_cpa" -ErrorAction SilentlyContinue Stop-Service -Name "bcs_wca" -ErrorAction SilentlyContinue Bcs-Log -LogMessage "Remove pass" -eventType Information $registryPath = "HKLM:\SOFTWARE\Barracuda\Content Shield"   If(Test-Path $registryPath) { $value1 = (Get-ItemProperty $registryPath).BCS_Admin -eq $null If ($value1 -eq $False) {Remove-ItemProperty -Path "$registryPath" -name BCS_Admin -Force | Out-Null} $value1 = (Get-ItemProperty $registryPath).BCS_InstallingSuite -eq $null If ($value1 -eq $False) {Remove-ItemProperty -Path "$registryPath" -name BCS_InstallingSuite -Force | Out-Null} $value1 = (Get-ItemProperty $registryPath).BCS_UpgradingSuite -eq $null If ($value1 -eq $False) {Remove-ItemProperty -Path "$registryPath" -name BCS_UpgradingSuite -Force | Out-Null} } } function Override-Pass { <# .SYNOPSIS Override tamper proof password with a preset password in order to be able to uninstall suite versions < 2.0. .DESCRIPTION In order to be able to uninstall the tamper proof protection needs to be disabled. This function disables the protection so that uninstall command can use the standard USER_PASS parameter 'password'. #> Bcs-Log -LogMessage "Try stopping BCS Services if installed.." -eventType Information Stop-Service -Name "bcs_cpa" -ErrorAction SilentlyContinue Stop-Service -Name "bcs_wca" -ErrorAction SilentlyContinue Bcs-Log -LogMessage "Override pass" -eventType Information $registryPath = "HKLM:\SOFTWARE\Barracuda\Content Shield" $name = "BCS_Admin" $value = "`$2a`$12`$CGmJgrD1dY3ZiUroiCLvDeozBM0eRYDpkL03d3q10kreJwndFEM1a" If(!(Test-Path $registryPath)) { # create new New-Item -Path $registryPath -Force | Out-Null New-ItemProperty -Path $registryPath -Name $name -Value $value -PropertyType String -Force | Out-Null } Else{ # update New-ItemProperty -Path $registryPath -Name $name -Value $value -PropertyType String -Force | Out-Null $value1 = (Get-ItemProperty $registryPath).BCS_InstallingSuite -eq $null If ($value1 -eq $False) {Remove-ItemProperty -Path "$registryPath" -name BCS_InstallingSuite -Force | Out-Null} $value1 = (Get-ItemProperty $registryPath).BCS_UpgradingSuite -eq $null If ($value1 -eq $False) {Remove-ItemProperty -Path "$registryPath" -name BCS_UpgradingSuite -Force | Out-Null} } } function Reset-Acls { # Reset folder permissions on locked down support folder $resetAcl = "icacls `"$($env:PROGRAMDATA)\Barracuda\Content Shield`" /RESET /T /Q" $process = (Start-Process 'cmd' -ArgumentList "/c","$($resetAcl)" -PassThru -Wait -NoNewWindow) Bcs-Log -LogMessage "Reset ACLs finished. Result: $($process.ExitCode)" -eventType Information } function Stop-BCSProcesses { Param( [Parameter(Mandatory=$true)] [array]$processes ) <# .SYNOPSIS Stop all processes in given list. .DESCRIPTION Terminates given list of processes. .PARAMETER processes Specifies the list of processes to terminate. #> $msg = 'Stop BCS processes if running' ForEach($pName in $processes){ $proc = Get-Process $pName -ErrorAction SilentlyContinue if ($proc) { Try{ # try gracefully first $proc.CloseMainWindow() # kill after five seconds Sleep 5 if (!$proc.HasExited) { $proc | Stop-Process -Force } Sleep 2 if (!$proc.HasExited) { taskkill /IM "$($pName).exe" /F } } Catch { $ErrorMessage = $_.Exception.Message $msg += "`nFailed to stop $pName - $ErrorMessage" } } else { $msg += "`nFailed to stop $pName. Not running." } } Bcs-Log -LogMessage $msg -eventType Information } function UninstallBCSPlus { <# .SYNOPSIS Uninstall Suite incl. clean up of left over components and files. .DESCRIPTION Remove and clean up BCS Suite installation. The machine should be left in a clean state after this. Make sure to reboot before re-installing the BCS Suite. #> #reset any acls Reset-Acls #remove tamperproof protection Remove-Pass $resultCode = 0 # Let's make sure we get the latest info about what is installed $installation_info = Get-BCSInstallation-Info $suite = $($installation_info['Suite']) Bcs-Log -LogMessage "Found $($suite.DisplayName) - $($suite.DisplayVersion)." -eventType Information If ($suite.UninstallString) { $installShieldLogPath = $(Join-Path $BASE_DIR InstallShield.log) $uninst = "$($suite.UninstallString) -silent -debuglog`"$($installShieldLogPath)`"" Bcs-Log -LogMessage " Uninstall: `"$($uninst)`"" -eventType Information $process = (Start-Process 'cmd' -ArgumentList "/c", "`"$($uninst)`"" -PassThru -Wait -NoNewWindow) $resultCode = $process.ExitCode Bcs-Log -LogMessage "Uninstall finished. Result: $($resultCode)" -eventType Information } # Check second time if we need a cleanup (retry) $installation_info = Get-BCSInstallation-Info $suite = $($installation_info['Suite']) If ($suite.UninstallString) { # For some reason the first uninstall attempt of the Suite did not succeed, so we try here again $uninst = "$($suite.UninstallString) -silent" $process = (Start-Process 'cmd' -ArgumentList "/c", $uninst -PassThru -Wait -NoNewWindow) $resultCode = $process.ExitCode Bcs-Log -LogMessage "2nd Try: Uninstall finished. Result: $($resultCode)" -eventType Information } # The following measures are brute force and only serve to clean up anything that a broken installatation cannot clean up. ########################################################################################################################## # REMARK: If this cleanup does not help to successfully reinstall (reboot before reinstall and also run tool at least twice before giving up), # it is worth to check for the Microsoft clean up tool for broken Windows Installers. # The registry management within windows installers is complicated and should not be lightheartedly muddled with. ########################################################################################################################## # Search for all components that are managed by Suite installer and remove them using their uninstall string ForEach($leftover in $installation_info.Keys){ $component = $($installation_info[$leftover]) Bcs-Log -LogMessage "Found $($component.DisplayName) - $($component.DisplayVersion)." -eventType Information If ($component.UninstallString) { # remove the single suite component leftovers $uninst = "$($component.UninstallString) /quiet /norestart" $process = (Start-Process 'cmd' -ArgumentList "/c","$uninst" -PassThru -Wait -NoNewWindow) Bcs-Log -LogMessage "Uninstall: $($uninst) finished with $($process.ExitCode)" -eventType Information } } # If the installation got really broken, make sure we check if there are any left over services. Remove these as well. $bcsServices = @("bcs_iot", "bcs_cpa", "bcs_wca", "bcs_fb") ForEach( $bcsServiceName in $bcsServices){ Try{ $service = Get-Service -Name $bcsServiceName -ErrorAction SilentlyContinue if ($service.Length -gt 0) { sc.exe delete "$bcsServiceName" } } Catch { $ErrorMessage = $_.Exception.Message Bcs-Log -LogMessage "Failed to delete $bcsServiceName - $ErrorMessage" -eventType Information } } # Only after services are removed from service controller, we can now force stop them without fearing they may be restarted by SVC # All processes in below list are force stopped, should they still be running. $bcsProcesses = @( "avupdate", "bcs-wca", "bcs-cpa", "filebeat", "bcs_iot", "BCSGui") Stop-BCSProcesses -processes $bcsProcesses # Finally, also check for any left over folders. $bcsFolders = @("$env:Programfiles\Barracuda\Content Shield", "$env:Programdata\Barracuda\Content Shield") ForEach( $folder in $bcsFolders){ If((Test-Path $folder)){ Try{ Remove-Item -Recurse -Force $folder } Catch { $ErrorMessage = $_.Exception.Message Bcs-Log -LogMessage "Failed to remove $folder - $ErrorMessage" -eventType Information } } } ExitWithCode -exitcode $resultCode } function Get-BCSInstallation-Info { <# .SYNOPSIS Get all installation state of BCS Suite components. .DESCRIPTION Returns the installation state of all BCS Suite components by iterating the registry. #> $info = @{} $bcsRegistryInfo = @{ "BCS Web Content Agent" = "WebFiltering" "BCS Content Protection Agent" = "MalwarePrevention" "BCS Content Shield GUI" = "Gui" "BCS Content Shield Support Tool" = "Support" "BCSFilebeat" = "Filebeat" "bcs_iot" = "iot" "Barracuda Content Shield Suite" = "Suite" } $suiteVer = Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall, HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall | Get-ItemProperty | Where-Object {($_.DisplayName -in $bcsRegistryInfo.Keys)} | Select-Object -Property DisplayName, DisplayVersion, UninstallString ForEach ($ver in $suiteVer) { $info["$($bcsRegistryInfo["$($ver.DisplayName)"])"] = $($ver) } $compVer = Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall, HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall | Get-ItemProperty | Where-Object {($_.DisplayName -in $bcsRegistryInfo.Keys)} | Select-Object -Property DisplayName, DisplayVersion, UninstallString ForEach ($ver in $compVer) { $info["$($bcsRegistryInfo["$($ver.DisplayName)"])"] = $($ver) } return $info } # get versions installed $installation_info = Get-BCSInstallation-Info $bcs_suite_installed=$($installation_info['Suite']).DisplayVersion if($installation_info.Count -gt 0){ Bcs-Log -LogMessage "Found Content Shield Suite Components:`n$(($installation_info.Keys | foreach { "$_ v$($($installation_info[$_]).DisplayVersion)" }) -join "`n")" -eventType Information } else { Bcs-Log -LogMessage "Found Content Shield Suite Components:`n -- 0 components installed. This machine is clean. --" -eventType Information } if ($ACTION -eq "install") { if($installation_info.Count -eq 0) { Bcs-Log -LogMessage "INSTALL BCS v$VERSION" -eventType Information InstallOrUpgradeBCSPlus } elseif($installation_info['Suite'] -eq ''){ Bcs-Log -LogMessage "Machine needs cleanup, Suite not installed but other Content Shield components are. Going to run cleanup now." -eventType Error $ACTION="remove" $bcs_suite_installed='' } else { if([version]$VERSION -gt [version]$bcs_suite_installed) { Bcs-Log -LogMessage "UPGRADE ($VERSION > $bcs_suite_installed)"-eventType Information InstallOrUpgradeBCSPlus } elseif([version]$VERSION -lt [version]$bcs_suite_installed) { Bcs-Log -LogMessage "NO ACTION (up to date: $VERSION < $bcs_suite_installed)" -eventType Information } elseif([version]$VERSION -eq [version]$bcs_suite_installed) { # NOTE: # If you run 'install' on the same version installed, it will try to uninstall the installed version. # If that is your intention, for that you need to pass the password parameter. Bcs-Log -LogMessage "NO ACTION (same: $VERSION == $bcs_suite_installed)" -eventType Information } } } if ($ACTION -eq "remove") { if ($installation_info.Count -gt 0) { Bcs-Log -LogMessage "UNINSTALL BCS..." -eventType Information UninstallBCSPlus } else{ Bcs-Log -LogMessage "NO ACTION (No BCS installed. No action necessary.)" -eventType Information } } if ($ACTION -inotin $actions) { Bcs-Log -LogMessage "UNKNOWN ACTION: $ACTION (Pick install | remove)" -eventType Error } ExitWithCode -exitcode 0