How can I catch Browsererrors like 504 in a SharePoint2013 Warm-Up Skript? - powershell

I am a freshman at PowerShell and I try to implement a SharePoint Warm-Up Skript.
This Skript is not originally mine, i only edited a existing one. Now it run nicely. But if a error occured like a DNS look up fail i want a Output in my Logfile like: "A DNS look up fail occured at URL..."
I want to catch the Browsererrors 400, 404 and 504. How can I catch them?
Here my code so far:
Function WarmUp() {
# Get URL list
Add-PSSnapIn Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
$was = Get-SPWebApplication -IncludeCentralAdministration
$was |? {$_.IsAdministrationWebApplication -eq $true} |% {$caTitle = Get-SPWeb $_.Url | Select Title}
# Warm up SharePoint web applications
Write-Host "Opening Web Applications..."
$global:ie = New-Object -Com "InternetExplorer.Application"
$global:ie.Navigate("about:blank")
$global:ie.Visible = $true
$global:ieproc = (Get-Process -Name iexplore)|? {$_.MainWindowHandle -eq $global:ie.HWND}
#Navigate here to all Applications, Collections and sites
# Close IE window
if ($global:ie) {
Write-Host "Closing IE"
$global:ie | Stop-Process -Force -ErrorAction SilentlyContinue
}
$global:ieproc | Stop-Process -Force -ErrorAction SilentlyContinue
# Clean Temporary Files
Remove-item "$env:systemroot\system32\config\systemprofile\appdata\local\microsoft\Windows\temporary internet files\content.ie5\*.*" -Recurse -ErrorAction SilentlyContinue
Remove-item "$env:systemroot\syswow64\config\systemprofile\appdata\local\microsoft\Windows\temporary internet files\content.ie5\*.*" -Recurse -ErrorAction SilentlyContinue
}
Function IENavigateTo([string] $url, [int] $delayTime = 500) {
# Navigate to a given URL
if ($url) {
if ($url.ToUpper().StartsWith("HTTP")) {
Write-Host " Navigating to $url"
try {
$global:ie.Navigate($url)
#If the certificate is invalid, bypass the error to show the context.
if ($global:ie.document.url -Match "invalidcert")
{
"Bypassing SSL Certificate Error Page";
$sslbypass=$global:ie.Document.getElementsByTagName("a") | where-object {$_.id -eq "overridelink"};
$sslbypass.click();
"Sleep for 5 seconds while final page loads";
start-sleep -s 5;
}
} catch {
try {
$pid = $global:ieproc.id
} catch {}
Write-Host " IE not responding. Closing process ID $pid"
$global:ie | Stop-Process -Force -ErrorAction SilentlyContinue
$global:ieproc | Stop-Process -Force -ErrorAction SilentlyContinue
$global:ie = New-Object -Com "InternetExplorer.Application"
$global:ie.Navigate("about:blank")
$global:ie.Visible = $true
$global:ieproc = (Get-Process -Name iexplore)|? {$_.MainWindowHandle -eq $global:ie.HWND}
}
IEWaitForPage $delayTime
}
}
}
Function IEWaitForPage([int] $delayTime = 500) {
# Wait for current page to finish loading
$loaded = $false
$loop = 0
$maxLoop = 20
while ($loaded -eq $false) {
$loop++
# Wait until the page is loaded.
While ($global:ie.ReadyState -ne 4) {
Start-Sleep -Seconds 2
Write-Host "Busy!"
}
if ($loop -gt $maxLoop) {
$loaded = $true
}
[System.Threading.Thread]::Sleep($delayTime)
# If the browser is not busy, the page is loaded
if (-not $global:ie.Busy)
{
$loaded = $true
}
}
}
#Main
if(-not(Test-Path "C:\Logs\SharePoint\WarmUpLogTest")){New-Item -ItemType Directory -Path "C:\Logs\SharePoint\WarmUpLogTest"}
Start-Transcript -Path ("C:\Logs\SharePoint\WarmUpLogTest\SPWarmUp{0:yyyy-MM-dd}.txt" -f $(get-date)) -Append
WarmUp
Stop-Transcript

If you run powershell V3 or newest you can add this on top of your IENavigateTo
try{invoke-webrequest $url}
catch{$_.errordetails|select -expand message}
without V3 you can try something like
$request = [System.Net.WebRequest]::Create($url)
try{$response = $request.GetResponse() }catch{$_.exception.message}

Related

Powershell: Delete class from JAR file with invoke-command

I am trying to remove vulnerable classes from the log4j jar file with powershell.
I am able to remove the file using the script locally on the server, however, I want to remove the class from many paths on many servers and trying to use invoke-command.
The script can open and read the JAR file but doesnt seem to action the delete() method. Is there a way to get powershell to delete the class "remotely"?
Here is my script:
$servers = #(
"server"
)
$class_to_delete = "JMSSink"
$unable_to_connect = #()
Add-Type -AssemblyName System.IO.Compression
Add-Type -AssemblyName System.IO.Compression.Filesystem
write-host "`nTesting connection to:" -ForegroundColor Yellow
$servers | ForEach-Object {Write-Host "$_"}
$servers | ForEach-Object {
$server = $_
try {
write-host "`nTesting $($server)"
Invoke-Command -ComputerName $server -ScriptBlock {
Write-Host "Connection successful to $($env:computername)" -ForegroundColor Green
} -ErrorAction Stop
} catch {
write-host "`nConnection failed to $($server)"
$unable_to_connect += $server
}
}
Write-Host "`nStarting script to remove $($class_to_delete) class from log4j" -ForegroundColor Yellow
$objects_skipped = #()
$servers | ForEach-Object {
$server_node = $_
write-host "`nPut in the file paths for $($server_node)" -ForegroundColor Yellow
$file_locations = (#(While($l=(Read-Host).Trim()){$l}) -join("`n"))
$file_locations | Out-File C:\temp\output.txt #Change this path to the temp folder and file on the server you execute from
$file_objects = Get-Content -Path C:\temp\output.txt #Change this path to the temp folder and file on the server you execute from
$stats_x = foreach ($file_object in $file_objects) {
$stats = Invoke-Command -ComputerName $server_node -ScriptBlock {
Write-Host "`nStarting on $($env:COMPUTERNAME)"
$class = $using:class_to_delete
Add-Type -AssemblyName System.IO.Compression
Add-Type -AssemblyName System.IO.Compression.Filesystem
$ful_path = $using:file_object
$fn = Resolve-Path $ful_path
try {
$zip = [System.io.Compression.ZipFile]::Open("$fn", "Read")
Write-Host "Backing up $($fn) to $($fn).bak"
$zip.Dispose()
Copy-Item "$fn" "$($fn).bak"
$zip = [System.io.Compression.ZipFile]::Open($fn, "Update")
$files = $zip.Entries | Where-Object { $_.name -eq "$($class).class" }
if (!$files) {
write-host "`nNo $($class) class found on $($env:COMPUTERNAME) for path: $($ful_path)"
$files.dispose()
$not_found = #()
$not_found += New-Object -TypeName PSObject -Property #{
Server = $env:COMPUTERNAME;
Path = $ful_path;
Result = "$($class) class NOT FOUND"
}
Write-Output $not_found
} else {
foreach ($file in $files) {
write-host "`n$($class) class found on $($env:COMPUTERNAME) for path: $($ful_path)"
write-host "`nDeleting file $($file)" -ForegroundColor Green
#delete class
$file.delete()
#check if class was successfully deleted
$confirm_delete = $zip.Entries | Where-Object { $_.name -eq "$($class).class" }
write-host $confirm_delete
if ($confirm_delete.name -match "$class.class") {
$deleted_status = "$($class) !!NOT REMOVED!!"
} else {
$deleted_status = "$($class) REMOVED"
}
$Output = #()
$Output += New-Object -TypeName PSObject -Property #{
Server = $env:COMPUTERNAME;
Path = $ful_path;
Result = $deleted_status
}
Write-Output $Output
}
}
} catch {
Write-Host "Cannot open $($ful_path) as a Zip File. $($Error[0])"
}
}
Write-Output $stats
}
$objects_skipped += $stats_x
}
#result
write-host "`nEnd result"
$objects_skipped | select Server,Result,Path | ft -AutoSize
You need to explicitly call Dispose() on the containing archive to persist the updates to the file on disk:
# this goes immediately after the `catch` block:
finally {
if($zip -is [IDisposable]){ $zip.Dispose() }
}
By placing the call to $zip.Dispose() inside a finally block, we ensure that it's always disposed regardless of whether an exception was thrown in the preceding try block.

Script is not picking all servers from the text file

$Servers = Get-Content "D:\Server\Servers.txt"
#Gathering Vars
$OSVersion = (Get-CimInstance Win32_OperatingSystem).version
$WarningPreference = 'SilentlyContinue'
$key = 'HKLM:\Software\Company\SCCMMetaData'
$StampCheck = (Get-ItemProperty -Path $key).PatchRelease
$data = foreach ($server in $Servers) {
#Gathering OS Version & Normalization of data
If ($OSVersion -like "10.*") { $OSName = "WINS2016-"; $KB = "KB4540670", "KB4538461", "KB4550929", "KB4556813", "KB4549949", "KB4550994", "KB4494175", "KB4540723" }
Elseif ($OSVersion -like "6.0.*") { $OSName = "WINS08R1-"; $KB = "KB4534303", "KB4534312" }
Elseif ($OSVersion -like "6.1.*") { $OSName = "WINS08R2-"; $KB = "KB4534314", "KB4539602", "KB4534310" }
Elseif ($OSVersion -like "6.2.*") { $OSName = "WINS12R1-"; $KB = "KB4541510", "KB4550917", "KB4556840", "KB4540726" }
Elseif ($OSVersion -like "6.3.*") { $OSName = "WINS12R2-"; $KB = "KB4541509", "KB4550961", "KB4556846", "KB4540725" }
#Check to see if KB is installed & build the stamp
Try {
$KBSearch = Get-HotFix -id $KB -ErrorAction Stop
$Stamp = $OSName
If ($StampCheck -eq "2020Q2") {
Return "$Server Already Compliant"
}
Else {
Set-ItemProperty -path 'HKLM:\Software\Company\SCCMMetaData' -Name 'PatchRelease' -Value $Stamp
Restart-Service -DisplayName 'Agent' -ErrorAction Ignore
Start-Sleep 30
Return "$Server Stamped"
}
}
Catch { Return "Missing Patch $KB on server $Server" }
}
Restart-Service -DisplayName ' Agent'
$data | Export-Csv "D:\Scripts\KB.csv" -NoTypeInformation
This is my code and it is not iterating for all the servers in the .txt file. It is only taking 1st server in the list .
It is not checking for each server in the txt file . Where i am doing the mistake ? Could any one help me ?
return will cause PowerShell to... well, return control to the caller :)
Simply omit the return keyword and leave the expressions following them as-is - they'll automatically "bubble up" to the caller anyway:
#Check to see if KB is installed & build the stamp
Try {
$KBSearch = Get-HotFix -id $KB -ErrorAction Stop
$Stamp = $OSName
If ($StampCheck -eq "2020Q2") {
"$Server Already Compliant"
}
Else {
Set-ItemProperty -path 'HKLM:\Software\Company\SCCMMetaData' -Name 'PatchRelease' -Value $Stamp
Restart-Service -DisplayName 'Agent' -ErrorAction Ignore
Start-Sleep 30
"$Server Stamped"
}
}
Catch { "Missing Patch $KB on server $Server" }
You can read more about the return keyword in the about_Return helpfile

Powershell script to install software

I am trying to make a powershell script that I can use with an RMM tool. So, basically this powershell script would be executed on the local machine. It would need to check to see if the version of the application is installed and at least version number xx. If not installed, or version is less, it would then download executable and silently install it.
I found an example online for Adobe Reader that does work, but it doesn't do the check before hand. So, this script would install Adobe Reader every time it is ran.
$tempFolder=$Env:TEMP
function runProcess ($cmd, $params) {
$p = new-object System.Diagnostics.Process
$p.StartInfo = new-object System.Diagnostics.ProcessStartInfo
$exitcode = $false
$p.StartInfo.FileName = $cmd
$p.StartInfo.Arguments = $params
$p.StartInfo.UseShellExecute = $False
$p.StartInfo.RedirectStandardError = $True
$p.StartInfo.RedirectStandardOutput = $True
$p.StartInfo.WindowStyle = 1;
$null = $p.Start()
$p.WaitForExit()
$output = $p.StandardOutput.ReadToEnd()
$exitcode = $p.ExitCode
$p.Dispose()
$exitcode
$output
}
#download installer
invoke-webrequest "ftp://ftp.adobe.com/pub/adobe/reader/win/AcrobatDC/1500720033/AcroRdrDC1500720033_en_US.msi" -OutFile "$tempFolder\AcroRdrDC1500720033_en_US.msi" -ErrorAction Stop
#run installer
$res = runProcess msiexec "/i $tempFolder\AcroRdrDC1500720033_en_US.msi /qb"
#check if return code is 0
if(0 -ne $res[0]){
return "Failed to install Adobe Reader: $($res[0]) $($res[1])"
}
#download the patch
invoke-webrequest "ftp://ftp.adobe.com/pub/adobe/reader/win/AcrobatDC/1502320070/AcroRdrDCUpd1502320070.msp" -OutFile "$tempFolder\AcroRdrDCUpd1502320070.msp" -ErrorAction Stop
#install it
$res = runProcess msiexec "/p $tempFolder\AcroRdrDCUpd1502320070.msp /qb"
#check if return code is 0
if(0 -ne $res[0]){
return "Failed to install Adobe Reader DC Patch: $($res[0]) $($res[1])"
}else{
#Attempt to silently disable the auto updater if set in this script
new-itemproperty "HKLM:\Software\Policies\Adobe\Acrobat Reader\DC\FeatureLockDown" -name bUpdater -value 0 -ErrorAction SilentlyContinue
}
I think there are some things that may not be needed in this script. It also doesn't have the check for version.
Found this on another site, but not sure how to implement it. Also, it doesn't look like it checks for version number.
function Is-Installed( $program ) {
$x86 = ((Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall") |
Where-Object { $_.GetValue( "DisplayName" ) -like "*$program*" } ).Length -gt 0;
$x64 = ((Get-ChildItem "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall") |
Where-Object { $_.GetValue( "DisplayName" ) -like "*$program*" } ).Length -gt 0;
return $x86 -or $x64;
}
Ideally, I would like to set the parameters at the top so I could use a template for other executables. For instance
$app_name
$app_version
$app_url
$app_filename
$app_executable
$app_arguments
Any help would be greatly appreciated.
I did some more digging and found this post: How to check if a program is installed and install it if it is not?
Which allows for matching Name and Version:
$tempdir = Get-Location
$tempdir = $tempdir.tostring()
$appName = '*AirParrot*'
$appVersion = "2.6.8"
$msiFile = $tempdir+"\microsoft.interopformsredist.msi"
$msiArgs = "-qb"
function Get-InstalledApps
{
if ([IntPtr]::Size -eq 4) {
$regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
}
else {
$regpath = #(
'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
}
Get-ItemProperty $regpath | .{process{if($_.DisplayName -and $_.UninstallString) { $_ } }} | Select DisplayName, Publisher, InstallDate, DisplayVersion, UninstallString |Sort DisplayName
}
$result = Get-InstalledApps | where {$_.DisplayName -like $appName -and $_.DisplayVersion -ge $appVersion}
If ($result -eq $null) {
(Start-Process -FilePath $msiFile -ArgumentList $msiArgs -Wait -Passthru).ExitCode
}

Making variables visible inside Powershell workflow

I have five variables defined before a workflow that need to be available to the workflow, but I can't find out how to do it.
Putting the variables inside the workflow makes them visible, but that causes an issue with the CSV import that means extra properties are added to the object relating to the workflow that I don't want.
Code as follows:
$source = 'C:\Users\Koozer\a place\'
$rotateParams = 90, 90, 270
$cropParams = #(64, 64), (32, 0)
$images = Import-Csv "${source}images.csv"
$colNames = $images[0].psobject.properties.Name
Workflow StitchCropWorkflow {
foreach -parallel ($imageSet in $images) {
$magickRotParams = ''
$n = 0
foreach ($image in $colNames) {
$magickRotParams += '`( '''+$source+$imageSet.($image)+''' -rotate '+$rotateParams[$n]+' `) '
$n++
}
$finfo = [io.fileinfo]$imagePathSets[0]
$command = 'magick '+$magickRotParams+' +append -crop '+$cropParams[0][0]+'x'+$cropParams[0][1]+'+'+$cropParams[1][0]+'+'+$cropParams[1][1]+' +repage '''+$finfo.DirectoryName+'\'+$finfo.BaseName+'_stitch_crop'+$finfo.Extension+''''
echo $command
Invoke-Expression $command
}
}
StitchCropWorkflow
You can pass parameters to a workflow like you would do it for a function:
$source = 'C:\Users\Koozer\a place\'
$rotateParams = 90, 90, 270
$cropParams = #(64, 64), (32, 0)
$images = Import-Csv "${source}images.csv"
$colNames = $images[0].psobject.properties.Name
Workflow StitchCropWorkflow {
param (
$source,
$rotateParams,
$cropParams,
$images,
$colNames
)
# your code here
}
StitchCropWorkflow $source $rotateParams $cropParams $images $colNames
If you define a variable in a script containing a workflow, to access the variable within the workflow the syntax is $using:variable
As an example, here is a powershell script containing a workflow with a single param.
param([string]$ComputerName)
$VerbosePreference = "Continue"
workflow Start-Reboot {
param($server)
Write-Verbose "Input server is $server"
InlineScript{
New-Item -Path C:\Scripts\Logging\Start-Reboot-Single.log -ItemType File -ErrorAction SilentlyContinue | Out-Null
}
Sequence{
InlineScript
{
Try
{
Get-WmiObject -ComputerName $using:server -Class Win32_Service -Filter "Name='HealthService'" | Invoke-WmiMethod -Name PauseService | Out-Null
$operatingSystem = Get-WmiObject Win32_OperatingSystem -ComputerName $using:server -ErrorAction stop
$LastReboot = [Management.ManagementDateTimeConverter]::ToDateTime($operatingSystem.LastBootUpTime).ToString().Trim()
Write-Verbose "Last reboot time for $using:server is $LastReboot"
Write-Verbose "Here we restart $using:server, for testing no reboot is done"
Restart-Computer -ComputerName $using:server -Wait -For Wmi -Force
$OSRecheck = Get-WmiObject Win32_OperatingSystem -ComputerName $using:server -ErrorAction stop
$CurrentReboot = [Management.ManagementDateTimeConverter]::ToDateTime($OSRecheck.LastBootUpTime).ToString().Trim()
$props = [Ordered]#{
Server=$using:server
LastReboot=$LastReboot
CurrentReboot=$CurrentReboot
}
} Catch {
Write-Verbose "Oh no, problem with $using:server"
$rnd = Get-Random -Minimum 1 -Maximum 5
Start-Sleep -Seconds $rnd
$err = $_.Exception.GetType().FullName
$props = [Ordered]#{
Server=$using:server
LastReboot=$err
CurrentReboot=$null
}
} Finally {
$object = New-Object -TypeName PSObject -Property $props
$random = Get-Random -Minimum 2 -Maximum 15
Start-Sleep -Seconds $random
Write-Output $object | Out-File -Append -FilePath C:\Scripts\Logging\Start-Reboot-Single.log
}
}#inline end
}#sequence end
}#end workflow block
Start-Reboot -server $ComputerName

Delete OST file and run outlook process

I have a form that im using to delete a ost file in powershell then starts outlook. I think the process to start outlook is opening quicker than the ost can be deleted. Here's what i have.
taskkill.exe /im outlook.exe /f
$OstPath = $Env:LocalAppData + "\Microsoft" + "\Outlook"
$ost = get-ChildItem $OstPath | where { $_.Extension -eq ".ost"}
$ost | remove-Item
Wait-Job $ost
Start-Process -FilePath "C:\Program Files\Microsoft Office 15\root\office15\outlook.exe"
This is the function i came up with and works really well with Office 2013 and Office 2016:
Function RebuildEmail
{
#COMMENT: Stops all Outlook process' and deletes .ost file then prompts to run setup.
$a = new-object -comobject wscript.shell
$intAnswer = $a.popup("This will stop Outlook and Skype for Business,`nRemoves the local copy of your email and will resynchronize from the exchange server. Be sure you have a steady internet connection! . `nDO YOU WANT TO CONTINUE?", `
0,"Service Desk",4)
If ($intAnswer -ne 6) {
$a.popup("Please call the Service Desk for support.")
} else {
$a.popup("Rebuilding will begin...")
Stop-Process -Name ("Outlook", "Lync")
$n = 0
Do
{
If(Get-Process -Name ("outlook", "Lync") -ErrorAction silentlycontinue)
{
Stop-Process -Name ("Outlook", "Lync") -ErrorAction SilentlyContinue
If ($n -lt 5)
{
$ExitLoop = $false
$n++
Start-Sleep -Seconds 3
If($n -eq 5){$ExitLoop = $true}
}
}
else {$ExitLoop = $true}
}
While ($ExitLoop -eq $false)
if ($n -ne 5)
{
$OstPath = $Env:LocalAppData + "\Microsoft" + "\Outlook"
$OSTs = Get-ChildItem $OstPath | where { $_.Extension -eq ".ost"}
$OSTs | Remove-Item
Start-Sleep -s 3
foreach ($ost in $OSTs)
{
$FilePath = $OstPath + "\" + $ost
$n = 0
Do
{
if(Test-Path $FilePath)
{
Remove-Item $FilePath
Start-Sleep -Seconds 5
$ExitLoop = $false
$n++
if($n -eq 5){$ExitLoop = $true}
}
else {$ExitLoop = $true}
}
While ($ExitLoop -eq $false)
if($n -ne 5)
{
$a = new-object -comobject wscript.shell
$intAnswer = $a.popup("The local copy of your email was successfully removed.", `
0,"Service Desk",0)
}
else
{
$a = new-object -comobject wscript.shell
$intAnswer = $a.popup("Unfortunatley, your local copy couldn't be removed. Please call the Service Desk.", `
0,"Service Desk",0)
}
}
}
else
{
$a = new-object -comobject wscript.shell
$intAnswer = $a.popup("Outlook was unable to close. Please call the Service Desk.", `
0,"Service Desk",0)
}
Start-Process -FilePath "C:\Program Files\Microsoft Office 15\root\office15\outlook.exe" -ErrorAction SilentlyContinue
Start-Process -FilePath "C:\Program Files (x86)\Microsoft Office\root\Office16\outlook.exe" -ErrorAction SilentlyContinue
Start-Process -FilePath "C:\Program Files\Microsoft Office 15\root\office15\lync.exe" -ErrorAction SilentlyContinue
Start-Process -FilePath "C:\Program Files (x86)\Microsoft Office\root\Office16\lync.exe" -ErrorAction SilentlyContinue
Start-Sleep -s 30
$a = new-object -comobject wscript.shell
$intAnswer = $a.popup("Please allow a few minutes to update folders. Call the Service Desk if issue persist.", `
0,"Service Desk",0)
}
}