281 lines
10 KiB
PowerShell
281 lines
10 KiB
PowerShell
<#
|
|||
|
|
|
||
|
|
.SYNOPSIS
|
||
|
|
Provides friendly messages reminding a user to restart their computer based on events that require it.
|
||
|
|
|
||
|
|
.DESCRIPTION
|
||
|
|
The Set-RebootSchedule script is called from any computer management software to notify a user of a required restart.
|
||
|
|
This script accepts several different parameters which change the messages displayed to the user.
|
||
|
|
|
||
|
|
.PARAMETER SysPerf
|
||
|
|
The -SysPerf parameter will remind the user to restart due to system performance.
|
||
|
|
This parameter should be called if the computer has had consistently high memory usage, recurring application hangs, or stuck processes.
|
||
|
|
If this parameter is used it will simply tell the user that a restart is needed to improve overall stability.
|
||
|
|
|
||
|
|
.PARAMETER OSUpdate
|
||
|
|
The -OSUpdate parameter will remind the user to restart due to operating system updates.
|
||
|
|
|
||
|
|
.PARAMETER Uptime
|
||
|
|
The -Uptime parameter will remind the user to restart due to long uptimes.
|
||
|
|
This script does not define the length of time the computer has been on, but will display the number of days to the end user.
|
||
|
|
If the computer has been on for 10 days and this parameter is used, it will tell the user this computer has been on for 10 days.
|
||
|
|
|
||
|
|
.PARAMETER AllowMinimize
|
||
|
|
The -AllowMinimize parameter will let the prompt be minimized by the user.
|
||
|
|
By default, the user is not allowed to minimize the window.
|
||
|
|
|
||
|
|
.EXAMPLE
|
||
|
|
Set-RebootSchedule -SysPerf
|
||
|
|
Prompt the user to restart because you've seen problems in multiple areas of the computer.
|
||
|
|
|
||
|
|
.EXAMPLE
|
||
|
|
Set-RebootSchedule -OSUpdate
|
||
|
|
Prompt the user to restart because Windows was recently updated.
|
||
|
|
|
||
|
|
.EXAMPLE
|
||
|
|
Set-RebootSchedule -Uptime
|
||
|
|
Prompt the user to restart because the computer has been up for a long time.
|
||
|
|
|
||
|
|
.NOTES
|
||
|
|
Calling this script with no parameters does nothing, the script will just exit without prompting the user for anything.
|
||
|
|
#>
|
||
|
|
|
||
|
|
## Define parameters
|
||
|
|
|
||
|
|
Param(
|
||
|
|
[switch]$SysPerf=$false,
|
||
|
|
[switch]$OSUpdate=$false,
|
||
|
|
[switch]$Uptime=$false,
|
||
|
|
[switch]$AllowMinimize=$false
|
||
|
|
)
|
||
|
|
|
||
|
|
## Get the name of this script
|
||
|
|
$ScriptName = $MyInvocation.MyCommand.Name
|
||
|
|
|
||
|
|
## Get a list of running PowerShell scripts
|
||
|
|
$RUnningPSScripts = Get-WmiObject Win32_Process -Filter "Name='powershell.exe'" | Select-Object ProcessId,CommandLine
|
||
|
|
|
||
|
|
## Loop through all other running instances of this script and kill each one that isn't the current instance.
|
||
|
|
ForEach ( $script in $RUnningPSScripts ) {
|
||
|
|
$OtherPID = $script.ProcessId
|
||
|
|
$OtherCMD = $script.CommandLine
|
||
|
|
|
||
|
|
## Check for other instances of this script that might be running
|
||
|
|
If ( ($OtherCMD -like "*${ScriptName}*") -and ($OtherPID -ne $PID) ) {
|
||
|
|
|
||
|
|
## Kill the other instance
|
||
|
|
Stop-Process -Id $script.ProcessId -Force
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
## Add Windows Forms support to this script
|
||
|
|
Add-Type -AssemblyName System.Windows.Forms
|
||
|
|
[System.Windows.Forms.Application]::EnableVisualStyles()
|
||
|
|
|
||
|
|
## Functions and subroutines
|
||
|
|
#region begin FUNCTIONS {
|
||
|
|
|
||
|
|
## Gets the number of days the system has been up.
|
||
|
|
Function Get-UptimeDays {
|
||
|
|
|
||
|
|
## Get a new instance of the Win32_OperatingSystem WMI object
|
||
|
|
$WMIOS = Get-WmiObject Win32_OperatingSystem
|
||
|
|
|
||
|
|
## Get the last boot time and convert it to a DateTime value
|
||
|
|
$LastBootTime = $WMIOS.ConvertToDateTime($WMIOS.LastBootUpTime)
|
||
|
|
|
||
|
|
## Get the number of days since the last time the system was powered on
|
||
|
|
[string]$Uptime = (New-TimeSpan -Start $LastBootTime -End $(Get-Date)).Days.ToString()
|
||
|
|
|
||
|
|
Return $Uptime
|
||
|
|
}
|
||
|
|
|
||
|
|
## Determines whether the logo can be downloaded or not
|
||
|
|
Function IsLogoDownloadable {
|
||
|
|
|
||
|
|
## Create a web request
|
||
|
|
$HTTPRequest = [System.Net.WebRequest]::Create($LogoURL)
|
||
|
|
|
||
|
|
## Get the response
|
||
|
|
Try { $HTTPResponse = $HTTPRequest.GetResponse() }
|
||
|
|
|
||
|
|
## Catch any error2 when trying to connect to the resource (including 404)
|
||
|
|
Catch [System.Net.WebException] { $HTTPResponse = $_.Exception.Response }
|
||
|
|
|
||
|
|
## Save the status code
|
||
|
|
$HTTPStatus = [int]$HTTPResponse.StatusCode
|
||
|
|
|
||
|
|
## Close the connection, because we're done with it
|
||
|
|
$HTTPResponse.Close()
|
||
|
|
|
||
|
|
## Test to make sure we received a status of 200 "OK" from the resource
|
||
|
|
If ($HTTPStatus -eq 200) { Return $true }
|
||
|
|
|
||
|
|
## If the status code is anything other than 200, return $false so we can do something else
|
||
|
|
Else { Return $false }
|
||
|
|
}
|
||
|
|
|
||
|
|
## Returns the local path of a file downloaded from a URL
|
||
|
|
Function Get-LogoPath {
|
||
|
|
|
||
|
|
## Get the file name from $url
|
||
|
|
$filename = [System.IO.Path]::GetFileName($LogoURL)
|
||
|
|
|
||
|
|
## Set the destination to download to
|
||
|
|
$localpath = "${Env:TEMP}\${filename}"
|
||
|
|
|
||
|
|
## Make sure the file is downloadable
|
||
|
|
If ( IsLogoDownloadable ) {
|
||
|
|
|
||
|
|
## Try downloading the file
|
||
|
|
Try { (New-Object Net.WebClient).DownloadFile($LogoURL, $localpath) }
|
||
|
|
|
||
|
|
## If you want to handle any download errors, put that code in here
|
||
|
|
Catch { $_ }
|
||
|
|
}
|
||
|
|
|
||
|
|
## Now that the file *may* be downloaded, check to make sure it exists
|
||
|
|
If ( Test-Path $localpath ) {
|
||
|
|
|
||
|
|
## Return $localpath to the caller
|
||
|
|
Return $localpath
|
||
|
|
}
|
||
|
|
|
||
|
|
## If $localpath doesn't exist (ie: from a previous execution of this script) then return $false
|
||
|
|
Else { Return $false }
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
## Builds the Windows form to display to the user
|
||
|
|
Function Display-Prompt([string]$message) {
|
||
|
|
|
||
|
|
## Set the form's overall parameters
|
||
|
|
$Form = New-Object System.Windows.Forms.Form
|
||
|
|
$Form.ClientSize = '400,360'
|
||
|
|
$Form.Text = $WindowTitle
|
||
|
|
$Form.TopMost = $true
|
||
|
|
$Form.MaximizeBox = $false
|
||
|
|
$Form.MinimizeBox = $AllowMinimize
|
||
|
|
$Form.FormBorderStyle = 'FixedDialog'
|
||
|
|
$Form.StartPosition = 'CenterScreen'
|
||
|
|
|
||
|
|
## Download the logo
|
||
|
|
$Logo = (Get-LogoPath)
|
||
|
|
|
||
|
|
## Add a picturebox to the form
|
||
|
|
If ( $Logo ) {
|
||
|
|
$pbLogo = New-Object system.Windows.Forms.PictureBox
|
||
|
|
$pbLogo.Width = $Form.ClientSize.Width - 20
|
||
|
|
$pbLogo.Height = 90
|
||
|
|
$pbLogo.Anchor = [System.Windows.Forms.AnchorStyles]::None
|
||
|
|
$pbLogo.Location = New-Object System.Drawing.Point(10,10)
|
||
|
|
$pbLogo.ImageLocation = $Logo
|
||
|
|
$pbLogo.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::CenterImage
|
||
|
|
$Form.Controls.Add($pbLogo)
|
||
|
|
}
|
||
|
|
|
||
|
|
## If the logo isn't available, add label to the form instead of a picturebox
|
||
|
|
Else {
|
||
|
|
$lblCompany = New-Object System.Windows.Forms.Label
|
||
|
|
$lblCompany.Text = $CompanyName
|
||
|
|
$lblCompany.AutoSize = $false
|
||
|
|
## The TextAlign enumerations can be found here:
|
||
|
|
## https://docs.microsoft.com/en-us/dotnet/api/system.drawing.contentalignment?view=netframework-2.0
|
||
|
|
$lblCompany.TextAlign = 32
|
||
|
|
$lblCompany.Width = $Form.ClientSize.Width - 20
|
||
|
|
$lblCompany.Height = 60
|
||
|
|
$lblCompany.Location = New-Object System.Drawing.Point(10,10)
|
||
|
|
$lblCompany.Font = 'Microsoft Sans Serif,24,style=Bold'
|
||
|
|
$Form.Controls.Add($lblCompany)
|
||
|
|
}
|
||
|
|
|
||
|
|
## This is the label that contains the reason for the restart
|
||
|
|
$lblReason = New-Object System.Windows.Forms.Label
|
||
|
|
$lblReason.Text = $message
|
||
|
|
$lblReason.AutoSize = $false
|
||
|
|
$lblReason.TextAlign = 1
|
||
|
|
$lblReason.Width = $Form.ClientSize.Width - 50
|
||
|
|
$lblReason.Height = 180
|
||
|
|
$lblReason.Location = New-Object System.Drawing.Point(25,100)
|
||
|
|
$lblReason.Font = 'Microsoft Sans Serif,11'
|
||
|
|
$Form.Controls.Add($lblReason)
|
||
|
|
|
||
|
|
## This is the label that contains a static message about how to proceed
|
||
|
|
$lblProceed = New-Object System.Windows.Forms.Label
|
||
|
|
$lblProceed.Text = $ProceedText
|
||
|
|
$lblProceed.AutoSize = $true
|
||
|
|
$lblProceed.TextAlign = 1
|
||
|
|
$lblProceed.Location = New-Object System.Drawing.Point(25,280)
|
||
|
|
$lblProceed.Font = 'Microsoft Sans Serif,11'
|
||
|
|
$Form.Controls.Add($lblProceed)
|
||
|
|
|
||
|
|
## This is the button that displays the nearest time to schedule a reboot
|
||
|
|
$btnRestartNow = New-Object System.Windows.Forms.Button
|
||
|
|
$btnRestartNow.Text = $RestartNowText
|
||
|
|
$btnRestartNow.Width = 130
|
||
|
|
$btnRestartNow.Height = 30
|
||
|
|
$btnRestartNow.Location = New-Object System.Drawing.Point(45,310)
|
||
|
|
$btnRestartNow.Font = 'Microsoft Sans Serif,11'
|
||
|
|
$btnRestartNow.Add_Click({
|
||
|
|
|
||
|
|
## Restart the computer
|
||
|
|
Restart-Computer
|
||
|
|
|
||
|
|
## We're done for now, just close the window.
|
||
|
|
$Form.Close()
|
||
|
|
})
|
||
|
|
|
||
|
|
$Form.AcceptButton = $btmRestartNow
|
||
|
|
$Form.Controls.Add($btnRestartNow)
|
||
|
|
|
||
|
|
## This is the placebo button, it just closes the form when clicked.
|
||
|
|
## The reminder schedule has already been created, so this button doesn't need to do anything.
|
||
|
|
$btnCancel = New-Object System.Windows.Forms.Button
|
||
|
|
$btnCancel.Text = $CancelText
|
||
|
|
$btnCancel.Width = 130
|
||
|
|
$btnCancel.Height = 30
|
||
|
|
$btnCancel.Location = New-Object System.Drawing.Point(225,310)
|
||
|
|
$btnCancel.Font = 'Microsoft Sans Serif,11'
|
||
|
|
$btnCancel.Add_Click({ $Form.Close() })
|
||
|
|
$Form.Controls.Add($btnCancel)
|
||
|
|
|
||
|
|
|
||
|
|
[void]$Form.ShowDialog()
|
||
|
|
}
|
||
|
|
|
||
|
|
#endregion FUNCTIONS }
|
||
|
|
|
||
|
|
## Define custom company name, logo, window title, and messages
|
||
|
|
#region begin CUSTOMIZATIONS {
|
||
|
|
|
||
|
|
## The following 2 settings are specific to your company
|
||
|
|
## This is the name of your company.
|
||
|
|
$CompanyName = "Emberkom"
|
||
|
|
## This is the URL to your company logo.
|
||
|
|
## If your company logo cannot be downloaded, $CompanyName will be used instead.
|
||
|
|
## If the image you specify is larger than 380x90, it will be centered in that area (clipping will occur).
|
||
|
|
$LogoURL = 'https://emberkom.s3.amazonaws.com/management/scripts/resources/small-web-logo.png'
|
||
|
|
|
||
|
|
## The following 7 settings control text displayed on the form when prompting the user to choose a time to restart their computer
|
||
|
|
## The title of the window
|
||
|
|
$WindowTitle = "Restart Required - ${CompanyName}"
|
||
|
|
## The message displayed to the user when -SysPerf is called.
|
||
|
|
$SysPerfText = "It looks like your computer isn't performing like it should.`n`nRestarting it will help to resolve some of the problems its been having."
|
||
|
|
## The message displayed to the user when -OSUpdate is called.
|
||
|
|
$OSUpdateText = "Good news!`n`nYour computer has recently installed some updates and it needs to be restarted soon."
|
||
|
|
## The message displayed to the user when -Uptime is called.
|
||
|
|
$UptimeText = "It looks like your computer has been on for more than $(Get-UptimeDays) days.`n`nIt's probably best to restart soon so it doesn't start causing problems for you."
|
||
|
|
## This is the message displayed just above the buttons - it should ask the user how they want to proceed
|
||
|
|
$ProceedText = "Can we make this happen?"
|
||
|
|
## This is the text for the cancel button. Clicking this button will just close the form
|
||
|
|
$CancelText = "Remind me later"
|
||
|
|
## The message on the button that immediately restarts the computer
|
||
|
|
$RestartNowText = "Restart Now"
|
||
|
|
|
||
|
|
#endregion CUSTOMIZATIONS }
|
||
|
|
|
||
|
|
## Display the form
|
||
|
|
If ( $SysPerf ) { Display-Prompt $SysPerfText }
|
||
|
|
ElseIf ( $OSUpdate ) { Display-Prompt $OSUpdateText }
|
||
|
|
ElseIF ( $Uptime ) { Display-Prompt $UptimeText }
|