observeit.psm1
<# observeit PowerShell module by Jonathan Boyko.
TODO: Install the Screenshot Optimizer. Change the component installation routines so that it's possible to refuse reinstall, similar to the credential testing Find a way to check whether the databases were installed correctly. Add activity indicator. Some jobs may take a while to complete, such as database upgrades. When removing leftovers, deleting folders with recurse Ensure the WCF HTTP Activation prerequisite is installed Add a switch to make the Application Server work as HTTPS rather than HTTP Application Server website seems to start in stopped state If installation for a component fails, try again, so that we don't need to ask for more credentials Version 0.9.1 - Get-OITInfoCollection now exports last 3 days of Windows Event Logs for System and Application logs. Version 0.9 - Added the Get-OITInfoCollection function which allows for collection of ObserveIT trace logs and configuration files. Version 0.8.2.2 - Fixed an issue where during uninstallation the currently installed products would appear after the uninstall process has finished, not before. - Fixed an issue with installatiion of SNAC. Version 0.8.2.1 - Fixed an issue where the SQL Native Client prerequisite would not get installed. Version 0.8.2 - The Install-OIT script now uses a different function to validate credentials. This time it should not throw up. - Fixed a few missing spaces. - Set the GUI mode to Full for the Web Categorization module installer, since the silent one seems buggy. - The Install-OIT script now installs SQL ODBC driver required for the observeit APIs. - Updated few issues with the Remove-OITInstalledComponents function. - Installing the SQL Native Client prerequisite. Version 0.8.1 2018-05-23 13:25:23 - Mitigated some error messages at the component removal phase. - The component removal script will now display currently installed components prior to removal. Version 0.8 2018-05-22 17:39:37 - The module now includes the Start-OITVisualDataRetention function which allows for retention of the screenshot data separately. - When testing for credentials, you now have the option to proceed with installation even if the credentials were detected as incorrect. Version 0.7.8 - The Remove-OITInstalledComponents script no longer removes the observeit Agent if one is installed. - The Remove-OITInstalledComponents script displays current database server address prior to removal of the components. - The Install-OIT script will not display the username it is running as. Version 0.7.7 2018-05-21 11:41:03 - The Install-OIT script now will open a log file for a component installation if the installation fails. - Fixed issue where check for the installer version would fail. Version 0.7.6 2018-05-21 11:26:02 - The Install-OIT script validates IIS prerequisites prior to installation. Version 0.7.5 2018-05-10 13:20:58 The script now retries the installation of a component if the installation fails. Version 0.7.4 2018-05-08 20:55:20 Prevent the script from removing the IIS settings for observeit. This just makes a mess. Version 0.7.3 2018-05-03 13:50:11 Various minor fixes and improvements: The Set-OITEncrypt and Set-OITDebug functions no longer change the encryption state of the Notification Service due to an apparent bug in the configuration file. You will have to alter it manually. The script now backs up the current configuration file prior to performing the change. The backed up file is stored in the same directory. Version 0.7.2 2018-05-02 12:17:08 After running Install-OIT, the function displays a list of currently installed products. Used to validate that the expected products were indeed installed. Version 0.7.1 2018-05-02 11:56:42 The Install-OIT function now checks whether we're installing observeit version 7.5 or higher. If we do, it installs the NodeJS prerequisite. Version 0.7.0 2018-05-01 15:38:12 Added the Get-OITFreeSpace function. This function tests specified drive on the server for free space. Either monitor space left in GB or percentage. Version 0.6.9 2018-04-26 15:29:20 The Web Console installation procedure now installs the Node.JS prerequisites. During removal, the script now cleans up the folders it can and removes IIS settings. During installation, the script configures IIS. You can also specify a custom port for the Application Server and Web Console during the installation. Version 0.6.6 y2018m04d24h15m41s41 Updated the Install-OIT function for easier setting of GUI level for the installer. Instead of specifying the level via the actual MSIEXEC switch, you can specify it via the function's own switch. Version 0.6.5 y2018m04d17h17m38s06 Apparently, previous fix did not resolve the issue with Web Categorization module services not stopping. This should work now. Version 0.6.4 y2018m04d17h14m20s23 Resolved an issue where Web Categorization module services would not be stopped prior to removal. #> function Test-OITCredentials { <# .SYNOPSIS Gets and validates credentials. .DESCRIPTION This function gets credentials from a user, validates the credentials with a domain controller, and then allows use of credentials by subsequent functions. #> $Global:OITCredential = Get-Credential Write-Host "Verifying credentials." $Global:OITUserName=$($($Global:OITCredential.GetNetworkCredential()).Domain + "\" + $($Global:OITCredential.GetNetworkCredential()).UserName) $Global:OITPassword=$($($Global:OITCredential.GetNetworkCredential()).Password) try { $CredentialsTest = Start-Process -FilePath cmd.exe /c -PassThru -Credential $Global:OITCredential -ErrorAction Stop Write-Host "Credential test succeeded." -ForegroundColor Green $Global:ValidationResult = $true } catch { Write-Host "Credentials test failed." -ForegroundColor Red } } function Install-OIT { <# .SYNOPSIS Installs observeit components. .DESCRIPTION This function installs observeit components. Use this during upgrade scenarios or brand new install scenarios. .PARAMETER Global:ApplicationServerPort Specify the path to the root of the observeit installer. For example: C:\observeit_Setup_vx.x.x.xx\observeit_Setup_vx.x.x.xx\. The script will then look for component installers under the root. .PARAMETER Global:DatabaseServer Specify the FQDN and, if needed, the port to the observeit database server. For example: oit.domain.lab, or oit.domain.lab,2104. .PARAMETER InstallDatabase Specifies whether you would like to install the database. This will either install the database from scratch or upgrade your current database. .PARAMETER InstallAppServer Specifies whether you would like to install the observeit Application Server. .PARAMETER InstallWebConsole. Specified whether you would like to install the observeit Web Console. .PARAMETER Global:GUILevel Specified the MSIEXEC GUI level for your install. Default is passive UI. Specify using MSIEXEC parameters, such as '/qn' .EXAMPLE Install-OIT -InstallerRoot C:\observeit_Setup_v7.4.1.27\observeit_Setup_v7.4.1.27\ -InstallAppServer -Global:DatabaseServer oit.domain.lab -InstallWebConsole -Global:GUILevel "/qn" .EXAMPLE Install-OIT -InstallerRoot C:\observeit_Setup_v7.4.1.27 -InstallAppServer -Global:DatabaseServer oit.domain.lab #> Param( [Parameter(Mandatory=$true,Position=1)] [string] $InstallerRoot, [Parameter(Mandatory=$true,Position=2)] [string] $DatabaseServer, [Parameter(Mandatory=$false,Position=3)] [switch] $InstallDatabase, [Parameter(Mandatory=$false,Position=4)] [switch] $InstallAppServer, [Parameter(Mandatory=$false,Position=5)] [switch] $InstallWebConsole, [Parameter(Mandatory=$false,Position=6)] [switch] $InstallWebCatModule, [Parameter(Mandatory=$false,Position=7)] [string] $ApplicationServerPort = "4884", [Parameter(Mandatory=$false,Position=8)] [string] $WebConsolePort = "443", [switch] $GUILevelFull, [switch] $GUILevelPassive, [switch] $GUILevelNone ) $Global:InstallerRoot = $InstallerRoot $Global:DatabaseServer = $DatabaseServer $Global:ApplicationServerPort = $ApplicationServerPort $Global:WebConsolePort = $WebConsolePort $Global:GUILevelFull = $GUILevelFull $Global:GUILevelPassive = $GUILevelPassive $Global:GUILevelNone = $GUILevelNone $Global:OITModulePath = $(Get-Module observeit).Path Test-Elevation do { Test-OITCredentials if ($Global:ValidationResult -eq $false) { $UserReply = Read-Host "The supplied credentials cannot be verified. Would you like to retry? (Otherwise continue) Y/N" if ($UserReply -eq "N") { $Global:ValidationResult = $true } } } while ($Global:ValidationResult -eq $false) Write-Host "Currently running in context of " -NoNewline Write-Host "$env:USERNAME" -ForegroundColor Yellow Install-OITPrerequisites # Check whether we're installing observeit 7.5 and higher # Yes, I know it'd dirty $CurrentVersion = $Global:InstallerRoot -match 'v\d.\d.\d.\d{1,3}' $CurrentVersion = $Matches[0] $CurrentVersion = $CurrentVersion.Replace("v","") $CurrentVersion = $CurrentVersion.Split(".") if ($CurrentVersion[0] -ge 7) { if ($CurrentVersion[1] -gt 4) { $InstallNodeJS = $true } else { $InstallNodeJS = $false } } # If no GUI level specified, assume the Passive mode if (!$Global:GUILevelFull -and !$Global:GUILevelPassive -and !$Global:GUILevelNone) { Write-Host "No GUI level specified. Assuming Passive." -ForegroundColor Yellow $Global:GUILevel = "/qr" } # Set the GUI level for MSIEXEC based on user preference if ($Global:GUILevelFull) { $Global:GUILevel = "/qf" } if ($Global:GUILevelPassive) { $Global:GUILevel = "/qr" } if ($Global:GUILevelNone) { $Global:GUILevel = "/qn" } $DBInstallerList = @{ "DB" = "$Global:InstallerRoot\DB\SQLPackage.exe" "DB_Analytics" = "$Global:InstallerRoot\DB_Analytics\SQLPackage.exe" } $Global:AppServerInstaller = "$Global:InstallerRoot\Web\AppServer\observeit.AppServerSetup.msi" $Global:NodeJSInstaller = "$Global:InstallerRoot\Web\PreRequisite_nodeServices.exe" $Global:SQLNCLIInstaller = "$Global:InstallerRoot\Web\sqlncli-2012-64-QFE.msi" $Global:WebConsoleInstaller = "$Global:InstallerRoot\Web\WebConsole\observeit.WebConsoleSetup.msi" $Global:WebCatInstaller = "$Global:InstallerRoot\WebsiteCat\WebsiteCat_Setup.msi" if ($InstallDatabase) { Write-Host "Installing the databases." foreach ($item in $DBInstallerList.Values) { Start-Process $item -ArgumentList "/server:$Global:DatabaseServer","/makedatabase","/quiet" -Wait } } # Verify the folders are there Write-Host "Verifying directory structure." $OITPaths = "$env:ProgramFiles\observeit","$env:ProgramFiles\observeit\Web" foreach ($item in $OITPaths) { if (!$(Test-Path $item)) { Write-Host "Creating directory" $item try { New-Item $item -ItemType Directory | Out-Null } catch { Write-Host "Failed to create the directory" $item } } } # Verify the IIS is there Write-Host "Verifying IIS structure." Import-Module WebAdministration $Global:ApplicationServerPort = ":" + $Global:ApplicationServerPort + ":" $Global:WebConsolePort = ":" + $Global:WebConsolePort + ":" $Global:ProductsToInstall = @() if ($InstallAppServer) { do { Install-OITAppServer Find-OITInstalledComponents if (!$($Global:InstalledProducts | Where-object {$_.ComponentName -eq "observeit Application Server"})) { &"$env:USERPROFILE\appdata\Local\Temp\AppServer_CA_Log.txt" Read-Host "Installation of the observeit Application Server failed. Press Enter key to retry" } } until ($($Global:InstalledProducts | Where-object {$_.ComponentName -eq "observeit Application Server"})) } if ($InstallWebConsole) { do { Install-OITWebConsole Find-OITInstalledComponents if (!$($Global:InstalledProducts | Where-object {$_.ComponentName -eq "observeit Console"})) { &"$env:USERPROFILE\appdata\Local\Temp\AppServer_CA_Log.txt" Read-Host "Installation of the observeit Web Console failed. Press Enter key to retry." } } until ($($Global:InstalledProducts | Where-object {$_.ComponentName -eq "observeit Console"})) } if ($InstallWebCatModule) { do { Install-OITWebCat Find-OITInstalledComponents if (!$($Global:InstalledProducts | Where-object {$_.ComponentName -eq "WebsiteCat"})) { &"$env:USERPROFILE\appdata\Local\Temp\WebsiteCat_CA_Log.txt" Read-Host "Installation of the observeit Web Categorization module failed. Press Enter key to retry." } } until ($($Global:InstalledProducts | Where-object {$_.ComponentName -eq "WebsiteCat"})) } Find-OITInstalledComponents Write-Host 'Currently installed components:' $Global:InstalledProducts $Global:OITCredential = $null } function Install-OITAppServer { $Global:ProductsToInstall += 'observeit Application Server' $AppServerAppPool = "IIS:\AppPools\observeitApplication" Write-Host "Installing observeit Application Server." if (!$(Test-Path $AppServerAppPool)) { New-Item $AppServerAppPool | Out-Null New-Item IIS:\Sites\observeitApplication -PhysicalPath 'C:\Program Files\observeit\Web\' -Bindings @{protocol="http";bindingInformation="$Global:ApplicationServerPort"} | Out-Null Set-ItemProperty IIS:\Sites\observeitApplication\ -Name applicationpool -Value observeitApplication | Out-Null } $ComponentInstallArguments = "/i",$Global:AppServerInstaller,$Global:GUILevel,"/norestart","DATABASE_SERVER=$Global:DatabaseServer","TARGETAPPPOOL=observeitApplication","TARGETSITE=observeitApplication","DATABASE_LOGON_TYPE=WindowsAccount","SERVICE_USERNAME=$Global:OITUserName","SERVICE_PASSWORD=$Global:OITPassword","/leo","AppServerMSI.log" Start-Process msiexec.exe -ArgumentList $ComponentInstallArguments -Wait -NoNewWindow } function Install-OITWebConsole { $Global:ProductsToInstall += 'observeit Console' $WebConsAppPool = "IIS:\AppPools\observeitWebConsole" $NodeJSPath = "C:\Program Files (x86)\nodejs" if ($InstallNodeJS -eq $true) { if (!$(Test-Path $NodeJSPath)) { Write-Host "Installing Web Console prerequisites." $ComponentInstallArguments = "/install","/passive","/norestart" Start-Process $Global:NodeJSInstaller -ArgumentList $ComponentInstallArguments -Wait } } Write-Host "Installing the SQL Native Client." $ComponentInstallArguments = "/i",$Global:SQLNCLIInstaller,$Global:GUILevel,"/norestart","IAcceptMSODBCSQLLicenseTerms=YES" Start-Process msiexec -ArgumentList $ComponentInstallArguments -Wait -NoNewWindow if (!$(Test-Path $WebConsAppPool)) { New-Item $WebConsAppPool | Out-Null New-Item IIS:\Sites\observeitWebConsole -PhysicalPath 'C:\Program Files\observeit\Web\' -Bindings @{protocol="https";bindingInformation="$Global:WebConsolePort"} | Out-Null Set-ItemProperty IIS:\Sites\observeitWebConsole\ -Name applicationpool -Value observeitWebConsole | Out-Null } $SQLDriver = "https://download.microsoft.com/download/E/6/B/E6BFDC7A-5BCD-4C51-9912-635646DA801E/msodbcsql_17.1.0.1_x64.msi" $SQLDriverInstaller = "msodbcsql_17.1.0.1_x64.msi" Write-Host "Downloading SQL ODBC Driver." try { Start-BitsTransfer $SQLDriver -ErrorAction Stop $SQLDriverDownloaded = $true } catch { Write-Host "Unable to download the SQL driver." } if ($SQLDriverDownloaded -eq $true) { Write-Host "Installing SQL ODBC Driver." $ComponentInstallArguments = "/i",$SQLDriverInstaller,$Global:GUILevel,"/norestart","IAcceptMSODBCSQLLicenseTerms=YES" Start-Process msiexec -ArgumentList $ComponentInstallArguments -Wait -NoNewWindow } Write-Host "Installing observeit Web Console." $ComponentInstallArguments = "/i",$Global:WebConsoleInstaller,$Global:GUILevel,"/norestart","DATABASE_SERVER=$Global:DatabaseServer","TARGETAPPPOOL=observeitWebConsole","TARGETSITE=observeitWebConsole","DATABASE_LOGON_TYPE=WindowsAccount","SERVICE_USERNAME=$Global:OITUserName","SERVICE_PASSWORD=$Global:OITPassword","/leo",".\WebConsoleMSI.log" Start-Process msiexec.exe -ArgumentList $ComponentInstallArguments -Wait -NoNewWindow } function Install-OITWebCat { $Global:ProductsToInstall += 'WebsiteCat' Write-Host "Installing observeit Website Categorization Module" # FIXME: This seems to stop all the services, rather than just the Web Categorization module Stop-OITServices -WebsiteCat | Out-Null Write-Host "Setting local firewall." $FWRuleAdd = New-NetFirewallRule -DisplayName "observeit Web Categorization module" -Direction Inbound –Protocol TCP –LocalPort 8000 -Action allow $ComponentInstallArguments = "/i",$Global:WebCatInstaller,"/qf","/norestart","DATABASE_SERVER=$Global:DatabaseServer","DATABASE_LOGON_TYPE=WindowsAccount","SERVICE_USERNAME=$Global:OITUserName","SERVICE_PASSWORD=$Global:OITPassword","/leo",".\WebCatMSI.log" Start-Process msiexec.exe -ArgumentList $ComponentInstallArguments -Wait -NoNewWindow } function Install-OITPrerequisites { Write-Host "Ensuring observeit prerequisites and installing as necessary." try { Install-WindowsFeature Web-Server, Web-WebServer, Web-Common-Http, Web-Default-Doc, Web-Dir-Browsing, Web-Http-Errors, Web-Static-Content, Web-Health, Web-Http-Logging, Web-Performance, Web-Stat-Compression, Web-Security, Web-Filtering, Web-App-Dev, Web-Net-Ext45, Web-Asp, Web-Asp-Net45, Web-ISAPI-Ext, Web-ISAPI-Filter, Web-Mgmt-Tools, Web-Mgmt-Console, NET-WCF-Services45, NET-WCF-HTTP-Activation45 –IncludeManagementTools -ErrorAction Stop | Out-Null } catch { Write-Host "Failed to install one of prerequisites." -ForegroundColor Red } try { Install-WindowsFeature NET-Framework-45-Core, NET-Framework-45-Features, NET-Framework-45-ASPNET -ErrorAction Stop | Out-Null } catch { Write-Host "Failed to install one of prerequisites." -ForegroundColor Red } } function Find-OITInstalledComponents { <# .SYNOPSIS Looks for observeit components installed on this machine. .DESCRIPTION Performs search in the registry to discover installed observeit components and saves their product IDs in a global variable. #> $Global:InstalledProducts = @() $Products = Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ | Get-ItemProperty | Where-Object {$_.Publisher -eq "observeit" -and $_.DisplayName -ne "ObserveITAgent"} foreach ($Product in $Products) { $CurrentObject = [PSCustomObject]@{ ComponentName = $Product.DisplayName ProductID = ($Product.UninstallString).Replace("MsiExec.exe /I","").Replace("MsiExec.exe /X","") } $Global:InstalledProducts += $CurrentObject } } function Remove-OITInstalledComponents ([switch]$Reboot) { <# .SYNOPSIS Uninstalls all observeit componenets. .DESCRIPTION Looks for observeit components installed on the current machine and uninstalls all of them. .EXAMPLE Remove-OITInstalledComponents -Reboot #> Test-Elevation Find-OITInstalledComponents if (!$Global:InstalledProducts) { Write-Host "No products are currently installed." -ForegroundColor Yellow } $Global:InstalledProducts Find-OITCurrentDBServer Write-Host "Stopping services:" -ForegroundColor Yellow Stop-OITServices -IIS -observeit -WebsiteCat foreach ($Component in $Global:InstalledProducts) { Write-Host "Currently removing" $Component.ComponentName Start-Process msiexec.exe -ArgumentList '/x',$Component.ProductID,'/qr' -Wait } Remove-OITLeftOverItems Write-Host "Currently installed products:" Find-OITInstalledComponents $Global:InstalledProducts Write-Host "Starting IIS:" -ForegroundColor Yellow Start-IIS if ($Reboot) { Restart-Computer -Force } if (!$Reboot) { Write-Host "Please note reboot may be required to successfully reinstall components." -ForegroundColor Yellow } } function Remove-OITLeftOverItems { <# .SYNOPSIS The synopsis goes here. This can be one line, or many. .DESCRIPTION The description is usually a longer, more detailed explanation of what the script or function does. Take as many lines as you need. .PARAMETER computername Here, the dotted keyword is followed by a single parameter name. Don't precede that with a hyphen. The following lines describe the purpose of the parameter: .PARAMETER filePath Provide a PARAMETER section for each parameter that your script or function accepts. .EXAMPLE There's no need to number your examples. .EXAMPLE PowerShell will number them for you when it displays your help text to a user. #> Write-Host "Cleaning up." -ForegroundColor Yellow # Look for path to an already-installed observeit install try { $item = Get-Item "$env:ProgramFiles\observeit" -ErrorAction Stop } catch { Write-Host "No default observeit installation path found." -ForegroundColor Yellow } if ($item) { if ($(Test-Path $item)) { Write-Host "Removing directory" $item try { #Remove-Item $item -Recurse -Force -Confirm:$false | Out-Null Get-ChildItem $item -Recurse -File | Remove-Item -Force -Confirm:$false -ErrorAction Stop | Out-Null Get-ChildItem $item -Recurse -Directory | Remove-Item -Force -Confirm:$false -ErrorAction Stop | Out-Null } catch { Write-Host "Failed to clean up item" $item -ForegroundColor Red } } } } function Find-OITCurrentDBServer { Find-OITPaths $CurrentResult = @() foreach ($item in $Global:OITBackendConfigFullPath) { try { $CurrentString = $(Get-Content $item -ErrorAction Stop| Select-String -Pattern '<add name="ConnectionString" connectionString="Data Source=') -match 'Data Source=(=?.*?;)' $CurrentString = $Matches[0] $CurrentString = $CurrentString.Replace('Data Source=','').Replace(';','') $CurrentResult += $CurrentString } catch { Write-Host "Could not find file " $item } } if ($CurrentResult) { Write-Host "Current database server address is " -NoNewline Write-Host "$($CurrentResult | Select-object -unique)" -ForegroundColor Yellow } else { Write-Host "Name of the current database server not found." -ForegroundColor Red } } function Test-Elevation { [boolean]$Global:IsElevated = $false $WindowsIdentity = [system.security.principal.windowsidentity]::GetCurrent() $Principal = New-Object System.Security.Principal.WindowsPrincipal($WindowsIdentity) $AdminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator if ($Principal.IsInRole($AdminRole)) { $Global:IsElevated = $true } if ($Global:IsElevated -ne $true) { Write-Host "Please use elevated PowerShell prompt." -ForegroundColor Yellow Break } } function Find-OITRunningServices { <# .SYNOPSIS Discovers running services. .DESCRIPTION Discovers all services related to observeit running on the current machine. This includes IIS and observeit services. #> $Global:StopIISServicesFilter = '$_.Name -notlike "observeit*" -and $_.Name -notlike "GCF1Service" -and $_.Name -notlike "WebsiteCat.Manager"' $Global:StopobserveitServicesFilter = '$_.Name -like "observeit*"' $Global:StopWebsiteCatServicesFilter = '$_.Name -like "GCF1Service" -or $_.Name -like "WebsiteCat.Manager"' $Global:StopWebsiteCatProcessFilter = '$_.ProcessName -like "gc*" -or $_.ProcessName -like "WebsiteCat*"' $Global:CurrentServices = @() $IISServices = "WMSVC","WAS","W3SVC","MSFTPSVC","IISADMIN","FTPSVC" foreach ($Service in $IISServices) { try { $Service = Get-Service $Service -ErrorAction Stop Write-Host "Service" $Service.Name "found." $Global:CurrentServices += $Service } catch { Write-Host "Service $Service not found. (That's okay, don't worry about it)" -ForegroundColor Gray } } try { $OITServices = Get-Service observeit* -ErrorAction Stop } catch { } try { $OITServices += Get-Service "GCF1Service","WebsiteCat.Manager" -ErrorAction Stop } catch { } foreach ($Service in $OITServices) { try { $Service = Get-Service $Service.Name -ErrorAction Stop Write-Host "Service" $Service.Name "found." $Global:CurrentServices += $Service } catch { Write-Host "Service $Service not found." -ForegroundColor Red } } } function Stop-OITServices ([switch]$IIS,[switch]$observeit,[switch]$WebsiteCat) { Find-OITRunningServices if ($observeit) { $CurrentServices = $Global:CurrentServices | Where-Object {Invoke-Expression $Global:StopobserveitServicesFilter} foreach ($Service in $CurrentServices) { try { Stop-Service $Service -Force -ErrorAction Stop Write-Host "Successfully stopped" $Service.Name -ForegroundColor Green } catch { Write-Host "Failed to stop service" $Service.Name -ForegroundColor Red } } } if ($WebsiteCat) { try { Get-Process | Where-Object {Invoke-Expression $Global:StopWebsiteCatProcessFilter} | Stop-Process -Force -ErrorAction Stop } catch { Write-Host "Failed to stop Web Categorization module processes." -ForegroundColor Red } $CurrentServices = $Global:CurrentServices | Where-Object {Invoke-Expression $Global:StopWebsiteCatServicesFilter} foreach ($Service in $CurrentServices) { try { Stop-Service $Service -Force -ErrorAction Stop Write-Host "Successfully stopped" $Service.Name -ForegroundColor Green } catch { Write-Host "Failed to stop service" $Service.Name -ForegroundColor Red } } } if ($IIS) { # TODO: This needs to be updated in the future. Having service names hard-coded is not the best idea. $CurrentServices = $Global:CurrentServices | Where-Object {Invoke-Expression $Global:StopIISServicesFilter} foreach ($Service in $CurrentServices) { try { Stop-Service $Service -Force -ErrorAction Stop Write-Host "Successfully stopped" $Service.Name -ForegroundColor Green } catch { Write-Host "Failed to stop service" $Service.Name -ForegroundColor Red } } } } function Start-IIS { $CurrentServices = $Global:CurrentServices | Where-Object {Invoke-Expression $Global:StopIISServicesFilter} foreach ($Service in $CurrentServices) { try { Write-Host "Starting service" $Service.Name Start-Service $Service.Name if ($(Get-Service $Service.Name).Status -eq "Running") { Write-Host "Successfully started service" $Service.Name -ForegroundColor Green } else { Write-Host "There seems to be an issue starting service" $Service.Name ". Please check service status." } } catch { Write-Host "Unable to start service" $Service.Name -ForegroundColor Red } } } function Test-ComputerAddress { Param( # Parameter help description [Parameter(Mandatory=$true,Position=1)] [string] $InputFile ) # This script expects a CSV file as an input with top row being a value 'Name'. Press any key to continue. # Jonathan Boyko, observeit Professional Services, 20180220t185553 # Initialize the object for data ingestion $MachineList = New-Object -TypeName psobject $MachineList | Add-Member -Type NoteProperty -Name "ComputerName" -Value "" $MachineList | Add-Member -Type NoteProperty -Name "IPAddress" -Value "" $MachineList | Add-Member -Type NoteProperty -Name "FirstOctet" -Value "" $MachineList | Add-Member -Type NoteProperty -Name "IsAlive" -Value $false foreach ($Computer in Get-Content $InputFile) { #Resolve address try { $ResolveResult = $(Resolve-DnsName -Name $Computer -Type A -ErrorAction Stop).IPaddress } catch { $ResolveResult = "Failed" } #Ping the address try { if ($ResolveResult -ne "Failed") { $PingResult = Test-NetConnection -ComputerName $ResolveResult -InformationLevel Quiet -ErrorAction Stop -WarningAction SilentlyContinue } } catch { $PingResult = "Failed" } $MachineList.ComputerName = $Computer $MachineList.IPAddress = $ResolveResult if ($MachineList.IPAddress -eq "Failed") { $PingResult = "Failed" } $MachineList.FirstOctet = $($MachineList.IPAddress.Split("."))[0] $MachineList.IsAlive = $PingResult $MachineList } } function Get-NestedMembership ([string]$SamAccountName) { $Identity = Get-ADUser -Identity $SamAccountName $Identity = $Identity.SamAccountName Import-Module ActiveDirectory $Groups = Get-ADPrincipalGroupMembership -Identity $Identity $FinalResult = $Groups foreach ($1stLevelGroup in $Groups) { $FinalResult += Get-ADPrincipalGroupMembership -Identity $Group foreach ($2ndLevelGroup in $1stLevelGroup) { $FinalResult += Get-ADPrincipalGroupMembership -Identity $2ndLevelGroup foreach ($3rdLevelGroup in $2ndLevelGroup) { $FinalResult += Get-ADPrincipalGroupMembership -Identity $3rdLevelGroup foreach ($4thLevelGroup in $3rdLevelGroup) { $FinalResult += Get-ADPrincipalGroupMembership -Identity $4thLevelGroup } } } } $FinalResult | Sort-Object -Unique } function Start-Stopwatch { $Global:StartTime = Get-Date } function Stop-Stopwatch { $StopTime = Get-Date $FinalTime = $StopTime-$Global:StartTime Write-Host $FinalTime.Hours "h" $FinalTime.Minutes "m" $FinalTime.Seconds "s" } function Test-OITHeartBeatLatency { <# .SYNOPSIS Tests latency to the observeit application server. .DESCRIPTION Uses the heartbeat sensor on the observeit application server to test connection latency between the client and the application server. .PARAMETER HeartBeatHost Hostname you would like to poll. The script will append the necessary string to query the Application Server status. .PARAMETER LongPollInterval What value, in milliseconds, would be considered too long/too high when polling the server. .PARAMETER ExpectedResult The function will stop execution when the expected result changes. For example, if we expect the application server to be online, the expected result would be 1. However, if we know that application server is down, the expected result will be 0. .PARAMETER OutputPath Output the result into a CSV file. .EXAMPLE Test-HeartBeatLatency -HeartBeatHost "https://oit.domain.lab:443/observeitapplicationserver/HeartBeat.asmx/IsAlive" -LongPollInterval 1000 -ExpectedResult 1 -OutputPath "c:\output.csv" #> Param( # URL we want to poll [Parameter(Mandatory=$true,Position=1)] [String[]] $HeartBeatHost, # How much is too long when polling the remote heartbeat. You can specify multiple URLs. [Parameter(Mandatory=$true,Position=2)] [int] $LongPollInterval, # Are we waiting for the Application Server to come back or go down? [Parameter(Mandatory=$true,Position=3)] [string] $ExpectedResult, [Parameter(Mandatory=$false,Position=4)] [string] $OutputPath ) add-type @" using System.Net; using System.Security.Cryptography.X509Certificates; public class TrustAllCertsPolicy : ICertificatePolicy { public bool CheckValidationResult( ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; } } "@ $AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12' [System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy $AverageArray = @() $CurrentAverage = 0 if (!$HeartBeatHost) { Write-Host "The URL is empty." -ForegroundColor Red Return } if ($ExpectedResult -eq "1") { $ExpectedResult = '*<string xmlns="http://observeit.WebServices/HeartBeat.asmx/">1</string>*' } if ($ExpectedResult -eq "0") { $ExpectedResult = '*<string xmlns="http://observeit.WebServices/HeartBeat.asmx/">0</string>*' } $CurrentOutput = New-Object psobject $CurrentOutput | Add-Member -Type NoteProperty -Name "PolledHost" -Value "" $CurrentOutput | Add-Member -Type NoteProperty -Name "PollDate" -Value "" $CurrentOutput | Add-Member -Type NoteProperty -Name "PollTime" -Value "" $CurrentOutput | Add-Member -Type NoteProperty -Name "CurrentLatency" -Value "" $CurrentOutput | Add-Member -Type NoteProperty -Name "AverageLatency" -Value "" $CurrentOutput | Add-Member -Type NoteProperty -Name "Exc" -Value "" $CurrentOutput | Add-Member -Type NoteProperty -Name "ExcCnt" -Value 0 $CurrentOutput | Add-Member -Type NoteProperty -Name "TotalPolls" -Value 0 $CurrentOutput | Add-Member -Type NoteProperty -Name "ExcRatio" -Value 0 Write-Host "Begin polling" Do { $CurrentOutput.TotalPolls++ foreach ($HBURL in $HeartBeatHost) { $ExecutionTime = Measure-Command -Expression {$Alive = Invoke-WebRequest -Uri $HBURL} $ExecutionTimeMS = $ExecutionTime.TotalMilliseconds $ExecutionTimeMS = [Math]::Round($ExecutionTimeMS, 0) $AverageArray += $ExecutionTimeMS $CurrentAverage = ($AverageArray | Measure-Object -Average).Average $CurrentAverage = [Math]::Round($CurrentAverage, 0) $CurrentOutput.Exc = "" $CurrentOutput.PolledHost = $HBURL.Replace("observeitapplicationserver/HeartBeat.asmx/IsAlive","") $CurrentOutput.PollDate = Get-Date -Format yyyy-MM-dd $CurrentOutput.PollTime = Get-Date -Format hh:mm:ss $CurrentOutput.CurrentLatency = $ExecutionTimeMS $CurrentOutput.AverageLatency = $CurrentAverage $CurrentOutput.ExcRatio = $CurrentOutput.ExcCnt / $CurrentOutput.TotalPolls $CurrentOutput.ExcRatio = [Math]::Round($CurrentOutput.ExcRatio,2) if ($ExecutionTimeMS -ge $LongPollInterval) { $CurrentOutput.Exc = "X" $CurrentOutput.ExcCnt++ } $CurrentOutput if ($OutputPath) { $CurrentOutput | Export-Csv $OutputPath -Append -NoTypeInformation } Start-Sleep -Seconds 5 } } While ($Alive.Content -like $ExpectedResult) } function Get-OITLinks ([switch]$AllLanguages) { <# .SYNOPSIS Retrieves observeit release download links. .DESCRIPTION This function parses the observeit download page for links to releases. .PARAMETER AllLanguages Specify this parameter to get links for all languages, not just the default English version. .EXAMPLE Get-OITLinks -AllLanguages #> $OITURI = "https://www.observeit.com/support/product_releases_download/" $RegEx = "http:\/\/.*observeit.*\d.zip" if ($AllLanguages) { $RegEx = "http:\/\/.*observeit.*.zip" } $URIs = (Invoke-WebRequest -Uri $OITURI).links | Where-Object {$_.href -like "*observeit_Setup*" -and $_.href -match $RegEx} $URIs.href $URIs.href | Select-Object -First 1 | Set-Clipboard } function Find-StringInFile { Param( [Parameter(Mandatory=$true,Position=1)] [String]$Path = "D:\Temp\Cases", [Parameter(Mandatory=$false,Position=2)] [String]$Keyword = "exception" ) $CurrentPath = $Path + "\*.*" Get-Content -Path $CurrentPath | Select-String -Pattern $Keyword } function Find-OITPaths { <# .SYNOPSIS Generates observeit paths. .DESCRIPTION This function generates paths for observeit Application Server(s) configuration files and saves the paths to a global variable. Useless on its own, it's designed to be a helper function for other functions. .EXAMPLE Find-OITPaths #> # Define variables, including full paths to the required config files as per documentation. $ProgramFiles = ${env:ProgramFiles} $OITInstallPath = $ProgramFiles + "\observeit\" $OITBackendConfigFiles = "web\observeitApplicationServer\web.config","web\observeit\web.config","RuleEngineService\bin\ActivityAlerts.Service.exe.config","HealthMonitor\bin\observeit.HealthMonitor.Service.exe.config"#,"NotificationService\observeit.WinService.exe.config" $OITAgentConfigFiles = "observeitAgent\Bin\bcplc.exe.config","observeitAgent\Bin\dlmonitor.exe.config","observeitAgent\Bin\rcdact.exe.config","observeitAgent\Bin\rcdcl.exe.config","observeitAgent\Bin\rcdsvc.exe.config","observeitAgent\Bin\svchostw.exe.config","observeitAgent\Bin\svcwtch.exe.config" $Global:OITBackendConfigFullPath = @(1..$OITBackendConfigFiles.Length) $Global:OITAgentConfigFullPath = @(1..$OITAgentConfigFiles.Length) # We're using the ArrayPosition variable during a loop to go through the list of install files and set full path for them $ArrayPosition = 0 # Start calculating full paths do { # Write the full paths into a global variable accessible from other functions $Global:OITBackendConfigFullPath.Item($ArrayPosition) = $OITInstallPath + $OITBackendConfigFiles.Item($ArrayPosition) $ArrayPosition = $ArrayPosition + 1 } while ($ArrayPosition -lt $OITBackendConfigFiles.Length) } function Start-OITConfigBackup { # This function just backs up configuration files $CurrentDateString = Get-Date -Format yyyymmddhhssffff foreach ($OITConfigFile in $Global:OITBackendConfigFullPath) { $BackupFileName = $OITConfigFile + ".$CurrentDateString" try { Copy-Item -Path $OITConfigFile -Destination $BackupFileName -Force -ErrorAction Stop } catch { Write-Host "File $BackupFileName not found." } } } function Restart-OITServices { <# .SYNOPSIS Restarts observeit services on the given machine #> $OITServices = Get-Service observeit* if ($OITServices) { Write-Host "Restarting observeit services..." -ForegroundColor Yellow try { Get-Service observeit* | Restart-Service -Force -ErrorAction Stop Write-Host "Done!" } catch { Write-Host "Failed!" } } if (!$OITServices) { Write-Host "No observeit services found!" -ForegroundColor Red } } function Restart-IIS { <# .SYNOPSIS Restarts IIS services on the given machine. #> $IISServices = Get-Service AppHostSVC,FTPSVC,IISADMIN,MSFTPSVC,W3SVC,WAS,WMSVC -ErrorAction SilentlyContinue -WarningAction SilentlyContinue if ($IISServices) { Write-Host "Restarting IIS services..." -ForegroundColor Yellow try { $IISServices | Restart-Service -Force -ErrorAction Stop Write-Host "Done!" -ForegroundColor Yellow } catch { Write-Host "Failed!" -ForegroundColor Red } } if (!$IISServices) { Write-Host "No IIS services found!" -ForegroundColor Red } } #### This block is used to monitor file system free space function Get-OITFreeSpace { <# .SYNOPSIS Checks for free space on the specified drive. .DESCRIPTION Will test the specified drive for amount of space and will produce a status in according to specification. It will then send an alert email to specified recipient via the specified mail host. It does not currently support SMTP authentication. .PARAMETER Volume Specify the drive letter of the drive to check. .PARAMETER MonitorPercent Specifies you want to check for percent of free space left on the drive. .PARAMETER MonitorGB Specifies you want to check for amount of GBs left on the drive. .PARAMETER LevelWarning Enter a number denoting percentage or amount of GB that would produce a warning. When the level of GBs or percent free goes below this number, the function will produce a warning status. .PARAMETER LevelCritical Enter a number denoting percentage or amount of GB that would produce a critical alert. When the level of GBs or percent free goes below this number, the function will produce a critical status. .PARAMETER Recipient Specifies recipient of the email to be sent with the alert. .PARAMETER MailFrom Specifies email of the sender. .PARAMETER MailHost Specified mail host to be used to send an email. .EXAMPLE Get-OITFreeSpace -Volume i: -MonitorPercent -LevelWarning 15 -LevelCritical 10 -Recipient ciso@domain.lab -MailFrom observeit@domain.lab -MailHost oit.domain.lab .EXAMPLE Get-OITFreeSpace -Volume i: -MonitorGB -LevelWarning 100 -LevelCritical 50 -Recipient ciso@domain.lab -MailFrom observeit@domain.lab -MailHost oit.domain.lab #> Param( # Volume letter [Parameter(Mandatory=$true)] [string] $Volume, # Declares whether you want to monitor CalculatedValue or size [Parameter(Mandatory=$false)] [switch] $MonitorPercent, # Declares whether you want to monitor amount of free GBs [Parameter(Mandatory=$false)] [switch] $MonitorGB, # Declare the monitoring threshold for Warning level [Parameter(Mandatory=$true)] [int] $LevelWarning, # Declare the monitoring threshold for Critical level [Parameter(Mandatory=$true)] [int] $LevelCritical, # Recipient of the email alert [Parameter(Mandatory=$true)] [string] $Recipient, # Sender [Parameter(Mandatory=$true)] [string] $MailFrom, # Address of the mail host [Parameter(Mandatory=$true)] [string] $MailHost ) $Volume = $Volume.Replace(":","") $Volume = $Volume.ToUpper() $CurrentDrive = Get-Volume -DriveLetter $Volume $CurrentHostName = $env:COMPUTERNAME if ($MonitorPercent) { $CalculatedValue = ($CurrentDrive.SizeRemaining / $CurrentDrive.Size) * 100 $CalculatedValue = [math]::Round($CalculatedValue) } if ($MonitorGB) { $CalculatedValue = $CurrentDrive.SizeRemaining $CalculatedValue = $CalculatedValue /1Gb $CalculatedValue = [math]::Round($CalculatedValue) } if ($CalculatedValue -gt $LevelWarning) { $CurrentLevel = "OK" } elseif ($CalculatedValue -le $LevelCritical) { $CurrentLevel = "Critical" } elseif ($CalculatedValue -le $LevelWarning) { $CurrentLevel = "Warning" } if ($MonitorPercent) { $FreeSpaceCheckResult = "The drive $Volume is in $CurrentLevel state. There's $CalculatedValue % of free space." } if ($MonitorGB) { $FreeSpaceCheckResult = "The drive $Volume is in $CurrentLevel state. There's $CalculatedValue GB of free space." } $FreeSpaceCheckSubject = "$CurrentHostName file system at $CurrentLevel level." $FreeSpaceCheckResult if ($CurrentLevel -eq "Warning" -or $CurrentLevel -eq "Critical") { Send-MailMessage -To $Recipient -From $MailFrom -Body $FreeSpaceCheckResult -Subject $FreeSpaceCheckSubject -SmtpServer $MailHost } } function Set-OITEncrypt { <# .SYNOPSIS Enables observeit encryption. .DESCRIPTION This function edits observeit configuration files in order to enable traffic encryption between observeit Application Server(s) and remote SQL server. .PARAMETER $EnableEncryption Has possible values of $true or $false, where $true enables the encryption and $false disables it. This parameter is False by default. .EXAMPLE Set-OITEncrypt -EnableEncryption $true #> # Define parameters. Basically, you have to say whether you want the encryption on or off Param( [Parameter(Mandatory=$true,Position=1)] [boolean]$EnableEncryption = $false ) # Run the function generating full paths Find-OITPaths Start-OITConfigBackup # Now, let's get to actually enabling/disabling encryption. # For each config file... foreach ($OITConfigFile in $Global:OITBackendConfigFullPath) { if ($OITConfigFile) { Write-Host "Nothing to work on." -ForegroundColor Red } Write-host $oitconfigfile Write-Host " " Write-Host "-----------------------------------------------------------" Write-Host " " Write-Host "Current config file is:" $OITConfigFile -ForegroundColor Gray Write-Host "Current string in file is:" -ForegroundColor Green # ...find the string for ConnectionStrings and present what's currently in the file to the user. Select-String -Path $OITConfigFile -Pattern '<add name="ConnectionString" connectionString="Data Source=' -CaseSensitive -SimpleMatch # Next, if user requested to disable encryption, let the user know that's what we're doing and disable it for the current file. If ($EnableEncryption -eq $false) { Write-Host " " Write-Host "Disabling encryption for file" $OITConfigFile -ForegroundColor Red Write-Host " " (Get-Content $OITConfigFile).Replace(";Encrypt=True","") | Out-File $OITConfigFile -Force # TODO: This here should be another parameter. (Get-Content $OITConfigFile).Replace(";TrustServerCertificate=True","") | Out-File $OITConfigFile -Force } # If, however, we're enabling encryption, enable it for the current file and let the user know. if ($EnableEncryption -eq $true) { Write-Host " " Write-Host "Enabling encryption for file" $OITConfigFile -ForegroundColor DarkGreen Write-Host " " (Get-Content $OITConfigFile).Replace("Integrated Security=SSPI","Integrated Security=SSPI;Encrypt=True") | Out-File $OITConfigFile -Force } # Finally, show the newly-applied string to the user. Write-Host "New string in file is:" -ForegroundColor Green Select-String -Path $OITConfigFile -Pattern '<add name="ConnectionString" connectionString="Data Source=' -CaseSensitive -SimpleMatch } Write-Host "Encryption is currently set to" $EnableEncryption -ForegroundColor Yellow Write-Host "Please also remember this script does not alter the encryption state of the Notification Service." -ForegroundColor Yellow } function Set-OITDebug { <# .SYNOPSIS Enables or disables observeit component debugging. .DESCRIPTION This function edits observeit configuration files to enable observeit Application Server(s) components debugging. .PARAMETER $EnableDebug Enables or disables debugging. Possible values are $true or $false .PARAMETER $LogLevel Sets the debugging log level. Default level is 3. Level 4 if the full debug logging, but produces very large files. .PARAMETER $Restartobserveit Mandatory. Specifies whether to restart observeit components to enable debugging. .PARAMETER $Restartobserveit Set to $true to restart IIS services as well. .EXAMPLE Set-OITDebug -EnableDebug $true -LogLevel 4 #> Param( [Parameter(Mandatory=$false,Position=1)] [boolean]$EnableDebug=$false, [Parameter(Mandatory=$false,Position=2)] [int]$LogLevel=3, [Parameter(Mandatory=$false,Position=3)] [switch]$Restartobserveit, [Parameter(Mandatory=$false,Position=4)] [switch]$RestartIIS ) # Run the function generating full paths Find-OITPaths Start-OITConfigBackup # Now, let's get to actually enabling/disabling encryption. # In each config file... foreach ($OITConfigFile in $Global:OITBackendConfigFullPath) { Write-Host "###############################################" Write-Host " " Write-Host " " Write-Host "Current config file is:" $OITConfigFile -ForegroundColor Gray Write-Host "Current string in file is:" -ForegroundColor Green # ...find the string for ConnectionStrings and present what's currently in the file to the user. Select-String -Path $OITConfigFile -Pattern '<add name="General" value="' -CaseSensitive -SimpleMatch # Next, if we were asked to enable debugging, do so. if ($EnableDebug -eq $true) { if ($LogLevel -eq 3) { Write-Host "Enabling debugging for file" $OITConfigFile -ForegroundColor Red (Get-Content $OITConfigFile).Replace('<add name="General" value="1" />','<add name="General" value="3" />') | Set-Content $OITConfigFile -Force Write-Host " " } if ($LogLevel -eq 4) { Write-Host "Enabling debugging for file" $OITConfigFile -ForegroundColor Red Write-Host " " Write-Host "Please remember this log level generates large trace files!" -ForegroundColor Red (Get-Content $OITConfigFile).Replace('<add name="General" value="1" />','<add name="General" value="4" />') | Set-Content $OITConfigFile -Force Write-Host " " } } # If requested to disable debug, proceed to look for all possible debug values and disable them. if ($EnableDebug -eq $false) { Write-Host "Disabling debugging for file" $OITConfigFile -ForegroundColor Green (Get-Content $OITConfigFile).Replace('<add name="General" value="4" />','<add name="General" value="1" />') | Set-Content $OITConfigFile -Force (Get-Content $OITConfigFile).Replace('<add name="General" value="3" />','<add name="General" value="1" />') | Set-Content $OITConfigFile -Force (Get-Content $OITConfigFile).Replace('<add name="General" value="2" />','<add name="General" value="1" />') | Set-Content $OITConfigFile -Force Write-Host " " } # Finally, write final string. Write-Host "New string in file is: " -ForegroundColor Green Select-String -Path $OITConfigFile -Pattern '<add name="General" value="' -CaseSensitive -SimpleMatch Write-Host " " } Write-Host "Processing done!" -ForegroundColor Yellow if ($Restartobserveit) { Restart-OITServices } if (!$Restartobserveit) { Write-Host "Please remember to restart observeit services to enable debugging!" -ForegroundColor Yellow Write-Host "Please also remember this script does not alter the debug level of the Notification Service." -ForegroundColor Yellow } if ($RestartIIS) { Restart-IIS } } function Start-OITVisualDataRetention { <# .SYNOPSIS Performs retention of observeit screenshot data. Written by Ze'ev Cohen - March 2018. .DESCRIPTION This utility scans the file system in order to delete screenshots of ObsreveIT leaving the most newer ones. .PARAMETER Path Specify the root path of observeit screenshot data storage. .PARAMETER DaysToKeep Specify the retention time, in days. .EXAMPLE Start-OITVisualDataRetention -Path C:\OITData\FS -DaysToKeep 90 #> Param( # Specify path [Parameter(Mandatory=$true,Position=0)] [string] $Path = "n/a", # Specify retention time [Parameter(Mandatory=$true,Position=1)] [int] $DaysToKeep = "n/a" ) # Manage command line parameters $numOfArgs = $args.Length-1 for ($i=0;$i -le $numOfArgs; $i++){ switch($args[$i]){ '-Path' { $i++ $Path = $args[$i] continue } '-DaysToKeep' { $i++ $DaysToKeep = $args[$i] continue } default { $ol = "Invalid Command-line Paramter: " + $args[$i] Write-Host $ol -ForegroundColor Yellow exit } } } Write-Output "" Write-Output "ObserveIT Del Screenshot v1.1" Write-Output "=============================" Write-Output "" # Check for PowerShell verison if ($PSVersionTable.WSManStackVersion.Major -lt 3){ Write-Out "PowerShell v3 or above should be installed on this server." exit; } if ($Path -eq "n/a" -Or $DaysToKeep -eq "n/a") { Write-Output "Usage: OITDelScreenShot <parameters as shown below>" Write-Output " " Write-Output ' <-Path "Path of ObserveIT images to scan and delete"> <-DaysToKeep # of days to keep the images, delete all older>' Write-Output "" Write-Output " Examples:" Write-Output "" Get-Help Start-OITVisualDataRetention -Examples exit } $path = $path.TrimEnd("\")+"\" if (Test-Path $path){ # Calculate the oldest date, delete all older $dt = (Get-Date).AddDays(-$DaysToKeep) Write-Host "Deleting Screen-Shots folders older than" $dt.ToString('yyyy\\M\\d') -ForegroundColor Cyan Write-Host "" $dt = [int](Get-Date $dt -Format ('yyyyMMdd')) $delCnt = 0 # Read the file-system folder structure, Years, Month, Day ###$years = (Get-ChildItem -Path $Path.ToString()).FullName | Sort #-Descending $years = dir -Directory $Path | Sort-Object -Property {$_.Name -as [int]} # Loop on the subfolder containing the Years for ($iy = 0; $iy -lt $years.Count ; $iy++) { $yearfolder = $years[$iy].FullName $yearmonths = dir -Directory $yearfolder | Sort-Object -Property {$_.Name -as [int]} # Loop each Year's folder for the Month it contains for ($im = 0; $im -lt $yearmonths.Count ; $im++) { $yearmonthfolder = $yearmonths[$im].FullName $yearmonth = $yearmonthfolder.Substring($path.Length) if ($yearmonth.Length -gt 5){ $y = [int]$yearmonth.Substring(0,4) $m = [int]$yearmonth.Substring(5) if (($m -gt 0) -and ($m -lt 13) -and ($m.Length -lt 3)){ $pathDateObj = [int](Get-Date -Year $y -Month $m -Day 1 -Format('yyyyMMdd')).ToString() Write-Host "`rChecking " $y $m " " -NoNewLine # Check if the Year-Month is in the Deletion Range required if ($pathDateObj -le $dt){ Write-Host "`rWill delete " $y $m " " -NoNewLine # Get a list of the Days for a specific Year-Month $days = dir -Directory $yearmonthfolder | Sort-Object -Property {$_.Name -as [int]} for ($id = 0; $id -lt $days.Count ; $id++) { $dd = $days[$id].FullName $d = [int]$dd.Substring($yearmonthfolder.Length+1) $delDay = [int](Get-Date -Year $y -Month $m -Day $d -Format('yyyyMMdd')).ToString() Write-Host "`rChecking " $delDay " " -NoNewLine # Check if the Year-Month-Day is in older than Deletion Range required if ($delDay -lt $dt){ Write-Host "`rDeleting: " $y $m $d $dayDel " " -NoNewLine $cmdExe = Start-Process -FilePath cmd.exe -ArgumentList "/c rd /S /Q $dd" -Wait -WindowStyle Hidden -PassThru $cmdErr = $cmdExe.ExitCode $delCnt ++ } } $emptydirectory = Get-Item -Path $yearmonthfolder if (!($emptydirectory.EnumerateFileSystemInfos() | Select-Object -First 1)) { $cmdExe = Start-Process -FilePath cmd.exe -ArgumentList "/c rd /S /Q $yearmonthfolder" -Wait -WindowStyle Hidden -PassThru $cmdErr = $cmdExe.ExitCode } } } } } $emptydirectory = Get-Item -Path $yearfolder if (!($emptydirectory.EnumerateFileSystemInfos() | Select-Object -First 1)) { $cmdExe = Start-Process -FilePath cmd.exe -ArgumentList "/c rd /S /Q $yearmonthfolder" -Wait -WindowStyle Hidden -PassThru $cmdErr = $cmdExe.ExitCode } } do { $dirs = Get-ChildItem $path -directory -recurse | Where-Object { (Get-ChildItem $_.fullName -Force).count -eq 0 } | Select-Object -expandproperty FullName $dirs | Foreach-Object { Remove-Item $_ } } while ($dirs.count -gt 0) Write-Host "" Write-Host "" Write-Host "Deleted: " $delCnt " Day(s)" } else { Write-Host "Screen-Shots folder does not exist" -ForegroundColor Red exit } } function Start-OITAppSrvParser { <# .SYNOPSIS Separates strings into objects. .DESCRIPTION This function splits observeit log strings into objects. .PARAMETER Path Specify path where textual log files are located. Cannot be empty. .PARAMETER Keyword Specifies which textual expression we are looking for. .PARAMETER FileType Specifies log file type we are looking for. .EXAMPLE Start-LogParserSeparator -Path C:\Logs -Keyword "available" -FileType "*.log" #> Param( [Parameter(Mandatory=$False,Position=1)] [String]$Path="C:\Program Files (x86)\observeit\observeitAgent", [Parameter(Mandatory=$False,Position=2)] [string]$Keyword, [Parameter(Mandatory=$False,Position=3)] [string]$FileType = "*.*", [Parameter(Mandatory=$False,Position=4)] [boolean]$QuickSearch = $true ) # The $FinalArray variable will be available globally, so other functions may use it as well. $global:FinalArray = @() $DateRegex = "\d{1,4}-\d{1,2}-\d{1,2}" $TimeRegex = "\d{1,2}:\d{1,2}:\d{1,2}.\d{1,3}" $ThreadRegex = "ThreadId: \d{1,3}" $EventTypeRegex = "\[.\]" $MessageRegex = "\d{1,4}-\d{1,2}-\d{1,2} \d{1,2}:\d{1,2}:\d{1,2}.\d{1,3} ThreadId: \d{1,3} \[.\]\s" # Generate the path to the log files. $FullPath = $Path + "\" +$FileType # If $Keyword variable is not empty... if ($Keyword) { # ...get contents of the log files. Get only the strings that match the pattern. Write-Host "Doing first data pass" $Data = Get-Content -Path $FullPath | Select-String -Pattern $DateRegex Write-Host "Doing second data pass" $Data = Get-Content -Path $FullPath | Select-String -Pattern $Keyword } # If, however, $Keyword variable is empty... if (!($Keyword)) { # ... get the data without any filters. Write-Host "Doing first data pass" $Data = Get-Content -Path $FullPath | Where-Object {$_ -match $DateRegex} } # BEGIN FOREACH LOOP # We will examine each string from the files we have read. Write-Host "Sorting data" foreach ($String in $Data) { # Split each into an object $CurrentObject = New-Object psobject $CurrentObject | Add-Member -Type NoteProperty -Name "Date" -Value $($String | Select-String -Pattern $DateRegex).Matches.Value $CurrentObject | Add-Member -Type NoteProperty -Name "Time" -Value $($String | Select-String -Pattern $TimeRegex).Matches.Value $CurrentObject | Add-Member -Type NoteProperty -Name "ThreadID" -Value $($String | Select-String -Pattern $ThreadRegex).Matches.Value $CurrentObject | Add-Member -Type NoteProperty -Name "EventType" -Value $($String | Select-String -Pattern $EventTypeRegex).Matches.Value # Leave only the message $String = $String -replace $DateRegex,"" $String = $String -replace $TimeRegex,"" $String = $String -replace $ThreadRegex,"" $String = $String -replace $EventTypeRegex,"" $CurrentObject | Add-Member -Type NoteProperty -Name "Message" -Value $String.Substring(3) $global:FinalArray += $CurrentObject } $global:FinalArray | Sort-Object -Property Date,Time } function Get-OITInfoCollection { <# .Synopsis Collects ObserveIT configuration files and trace data. .DESCRIPTION Searches the specified ObserveIT folder for configuration and trace files, and zips them into a single file for easy transfer. .PARAMETER OITInstallationPath Root path where ObserveIT is installed. Default is C:\Program Files\ObserveIT. .PARAMETER DestinationPath Path to where the resulting file should be saved. Default is current profile's TEMP folder. .EXAMPLE Get-OITInfoCollection -OITInstallationPath 'C:\Program Files\ObserveIT' -DestinationPath C:\Temp #> param ( # Path to the ObserveIT installation folder [Parameter(Mandatory=$false)] [string] $OITInstallationPath = 'C:\Program Files\ObserveIT', # Destination for the zip file output [Parameter(Mandatory=$false)] [string] $DestinationPath = $env:TEMP ) $FilesToSearchFor = @( "*.config", "*.txt", "*.log" ) $CollectorFileName = $env:COMPUTERNAME + "_" + "OITLogCollector$(Get-Date -Format yyyymmddhhmmss)" $TempFolder = $env:TEMP + '\' + $CollectorFileName $DestinationPath = $DestinationPath + '\' + $CollectorFileName Write-Host "Testing availability of the destination path: $OITInstallationPath" $InstallationExists = Test-Path $OITInstallationPath if ($InstallationExists -eq $false) { Write-Host "FATAL ERROR: ObserveIT installation path does not exists or no perimssions to access." break } Write-Host 'Creating a temporary folder.' try { New-Item $TempFolder -ItemType Directory -Force -ErrorAction Stop | Out-Null Write-Host 'Done.' } catch { Write-Host 'FATAL ERROR: Unable to create a folder at the following path:' $TempFolder Write-Host 'Quitting.' break } Write-Host "Collecting files." foreach ($item in $FilesToSearchFor) { $FilesToCopy = Get-ChildItem $OITInstallationPath\* -Recurse -Include $item -ErrorAction Ignore -Exclude 'LICENSE.txt' foreach ($file in $FilesToCopy) { $ParentFolderName = $file.fullName -split ('\\') $ArrayLength = $ParentFolderName.Length - 2 $ParentFolderName = $ParentFolderName[$ArrayLength] $ParentFolderPath = $TempFolder + '\' + $ParentFolderName if (!$(Test-Path $ParentFolderPath)) { New-Item $ParentFolderPath -ItemType Directory -Force | Out-Null } try { Copy-Item $file -Destination $ParentFolderPath -ErrorAction Stop } catch { Write-Host "Error copying item" $file -ForegroundColor Red } } } Write-Host 'Export Windows Event Log data' Get-EventLog Application -After $((Get-Date).AddDays(-3)) | fl > $TempFolder\EventLogApplication.log Get-EventLog System -After $((Get-Date).AddDays(-3)) | fl > $TempFolder\EventLogApplication.log Write-Host "Compressing files to the following path: $DestinationPath.zip" Compress-Archive -Path $TempFolder -DestinationPath $DestinationPath -CompressionLevel Optimal -Force Remove-Item $TempFolder -Recurse -Force } |