2023-10-05 13:11:38 -04:00
# Define the MSP name
2021-08-05 09:42:15 -04:00
[ string ] $Global:MSPName = "Emberkom"
2023-10-05 13:11:38 -04:00
# This section will define several global variables that can be used in scripts that source this one
2021-08-05 09:42:15 -04:00
[ string ] $Global:RootDirectory
[ string ] $Global:ScriptsDirectory
[ string ] $Global:OutputDirectory
[ string ] $Global:ToolsDirectory
[ string ] $Global:LogsDirectory
[ string ] $Global:LogFile
2020-09-09 21:06:44 -04:00
2023-09-15 16:46:10 -04:00
# Define the root path for the MSP in the Windows Registry
[ string ] $Global:MSPRegistryRoot = "HKEY_LOCAL_MACHINE\SOFTWARE\ ${MSPName} "
2023-10-05 13:11:38 -04:00
# Setup the MSP management directories
2021-08-05 09:42:15 -04:00
Function Setup-MSPDirectories {
If ( Test-Path ${ Env : ProgramData } ) { $Global:RootDirectory = " ${Env:ProgramData} \ ${MSPName} " } Else { $Global:RootDirectory = " ${Env:SystemDrive} \ ${MSPName} " }
$Global:ScriptsDirectory = " ${RootDirectory} \Scripts"
$Global:OutputDirectory = " ${RootDirectory} \Output"
$Global:ToolsDirectory = " ${RootDirectory} \Tools"
$Global:LogsDirectory = " ${RootDirectory} \Logs"
2020-09-09 21:06:44 -04:00
2023-10-05 13:11:38 -04:00
# Make sure all the management directories exist
2021-08-05 09:42:15 -04:00
$RootDirectory , $ScriptsDirectory , $OutputDirectory , $ToolsDirectory , $LogsDirectory | ForEach-Object {
If ( !( Test-Path $_ ) ) {
Try { New-Item -Path $_ -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null }
Catch { Write-Output $_ . Exception . Message }
}
}
2020-09-23 21:31:52 -04:00
}
2020-09-09 21:06:44 -04:00
2023-10-05 13:11:38 -04:00
# Function to get a datestamp for right now in a long format
2021-08-05 09:42:15 -04:00
Function Get-LongDateTime {
Return $ ( Get-Date -Format "yyyyMMddHHmmssffff" )
}
2020-09-19 15:29:31 -04:00
2023-10-05 13:11:38 -04:00
# Setup the log file for messages generated when this script is run
2021-08-05 09:42:15 -04:00
Function Setup-LogFile {
$LogFilePrefix = "ManagementScript"
$LogFilePostfix = Get-LongDateTime
$Global:LogFile = " ${LogsDirectory} \ ${LogFilePrefix} _ ${LogFilePostfix} .log"
2023-10-05 13:11:38 -04:00
# Delete log files older than 120 days
2021-09-23 14:28:01 -04:00
$LogFileAllowedAge = ( Get-Date ). AddDays ( -120 )
2021-08-05 09:42:15 -04:00
Try {
Get-ChildItem -Path " ${LogsDirectory} \ ${LogFilePrefix} _*.*" -Filter '*.log' | `
Where-Object { $_ . LastWriteTime -lt $LogFileAllowedAge } | `
Remove-Item -Force -ErrorAction SilentlyContinue | Out-Null
}
Catch { Write-Output $_ . Exception . Message }
Try { If ( !( Test-Path $LogFile ) ) { New-Item -Path $LogFile -ItemType File -Force | Out-Null } }
Catch { Write-Output $_ . Exception . Message }
}
2021-05-27 18:07:02 -04:00
2023-10-05 13:11:38 -04:00
# Log a message to the log file
2023-10-05 13:39:07 -04:00
# IMPORTANT: In Powershell 'echo' is an alias for Write-Output, which is overriden when this Tools.ps1 file is called before your script.
# To prevent unwanted behavior, if you need to echo something do the following instead:
# Start-Process "cmd.exe" -ArgumentList '/C echo y | some-command'
2023-10-05 13:11:38 -04:00
Function Write-Output {
2020-11-06 11:24:16 -05:00
param ([ Parameter ( Mandatory = $true , ValueFromPipeline = $true )][ string ] $Message )
2023-10-05 14:09:27 -04:00
Add-Content -Path $Global:LogFile -Value " $( Get-Date -Format "yyyy-MM-dd HH:mm:ss" ) - ${Message} "
Microsoft . Powershell . Utility \ Write-Output $Message
2020-09-09 21:06:44 -04:00
}
2020-10-22 14:01:35 -04:00
2023-10-05 13:11:38 -04:00
# Log an error to the log file
Function Write-Error {
2020-11-06 11:24:16 -05:00
param ([ Parameter ( Mandatory = $true , ValueFromPipeline = $true )][ string ] $Message )
2023-10-05 14:09:27 -04:00
Add-Content -Path $Global:LogFile -Value " $( Get-Date -Format "yyyy-MM-dd HH:mm:ss" ) - Error: ${Message} "
Microsoft . Powershell . Utility \ Write-Error $Message
2020-09-07 13:43:37 -04:00
}
2023-10-05 13:11:38 -04:00
# Function to determine if the current user context is an Administrator
2020-11-06 11:24:16 -05:00
Function IsAdmin {
If ( ([ Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity ]:: GetCurrent ()). IsInRole ([ Security.Principal.WindowsBuiltInRole ]:: Administrator ) )
{ Return $true } Else { Return $false }
2020-09-07 13:43:37 -04:00
}
2023-10-05 13:11:38 -04:00
# Run a script from the live repo or from a local path
2020-11-06 11:24:16 -05:00
Function Run-Script {
param (
2023-10-05 13:11:38 -04:00
# Parameters for live-ps scripts
2020-11-06 11:24:16 -05:00
[ Parameter ( Mandatory = $false , ParameterSetName = 'live-ps' )]
[ string ] $Type = 'management-scripts' ,
[ Parameter ( Mandatory = $true , ParameterSetName = 'live-ps' )]
[ string ] $LivePSScript ,
2023-10-05 13:11:38 -04:00
# Parameters for local scripts
2020-11-06 11:24:16 -05:00
[ Parameter ( Mandatory = $true , ParameterSetName = 'local' )]
[ string ] $Path ,
2023-10-05 13:11:38 -04:00
### $NoWait will prevent waiting for the specified script to finish running
2020-11-06 11:24:16 -05:00
[ Parameter ( Mandatory = $false , ParameterSetName = 'local' )]
[ switch ] $NoWait = $false ,
2023-10-05 13:11:38 -04:00
# Parameters for all scripts
2020-11-06 11:24:16 -05:00
[ Parameter ( Mandatory = $false , ParameterSetName = 'live-ps' )]
[ Parameter ( Mandatory = $false , ParameterSetName = 'local' )]
[ string ] $Arguments = 'null' ,
2023-10-05 13:11:38 -04:00
### $HaltIfRunning is a collection of app names which, if running, will prevent the script from running
2020-11-06 11:24:16 -05:00
[ Parameter ( Mandatory = $false , ParameterSetName = 'live-ps' )]
[ Parameter ( Mandatory = $false , ParameterSetName = 'local' )]
[ string[] ] $HaltIfRunning = 'null' ,
2023-10-05 13:11:38 -04:00
### $ForceClose will forcefully close all apps specified in $HaltIfRunning
2020-11-06 11:24:16 -05:00
[ Parameter ( Mandatory = $false , ParameterSetName = 'live-ps' )]
[ Parameter ( Mandatory = $false , ParameterSetName = 'local' )]
[ switch ] $ForceClose = $false
)
If ( $Arguments -eq "null" ) { $Arguments = $null }
If ( $HaltIfRunning -eq "null" ) { $HaltIfRunning = $null }
If ( $LivePSScript ) {
$URL = "https://dev.emberkom.com/emberkom/ ${Type} /raw/branch/master/ ${LivePSScript} .ps1"
2023-10-05 13:11:38 -04:00
If ( !( IsURLValid -URL $URL ) ) { Write-Output "The specified script does not exist: ${LivePSScript} " ; Exit }
2020-11-06 11:24:16 -05:00
}
2023-10-05 13:11:38 -04:00
If ( $Path ) { If ( !( Test-Path $Path ) ) { Write-Output "The specified script does not exist: "" ${Path} """ ; Exit } }
If ( ( $null -ne $HaltIfRunning ) -and ( $ForceClose ) -and ( !( IsAdmin ) ) ) { Write-Output "Cannot close dependent apps because this task does not have administrative privileges" ; Exit }
2020-11-06 11:24:16 -05:00
If ( $HaltIfRunning ) {
$Halt = $false
2023-10-05 13:11:38 -04:00
Write-Output "Checking for running instances of the following dependent apps:"
2020-11-06 11:24:16 -05:00
ForEach ( $name in $HaltIfRunning ) {
2023-08-23 14:50:47 -04:00
$RunningInstances = Get-Process | Where-Object { $_ . Name -like " ${name} " }
2020-11-06 11:24:16 -05:00
If ( $RunningInstances ) {
If ( $ForceClose -eq $true ) {
2023-10-05 13:11:38 -04:00
Try { $name | Stop-Process -Force ; Write-Output " "" ${name} "" (force closed)" }
Catch { Write-Error $_ . Exception . Message ; Exit }
2020-11-06 11:24:16 -05:00
}
2023-10-05 13:11:38 -04:00
Else { $Halt = $true ; Write-Output " "" ${name} "" (running)" }
2020-09-23 21:31:52 -04:00
}
2023-10-05 13:11:38 -04:00
Else { Write-Output " "" ${name} "" (not running)" }
2020-09-23 20:43:29 -04:00
}
2023-10-05 13:11:38 -04:00
If ( $Halt -eq $true ) { Write-Output "Dependent apps are currently in use and are preventing the specified script from running" ; Exit }
2020-09-23 15:24:26 -04:00
}
2020-11-06 11:24:16 -05:00
switch ( $PSCmdlet . ParameterSetName ) {
'live-ps' {
2023-10-05 13:11:38 -04:00
Write-Output "Retrieving : ${LivePSScript} .ps1"
2020-11-06 11:24:16 -05:00
Try { $Script = ( New-Object Net . WebClient ). DownloadString ( $URL ) }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message ; Exit }
2020-11-06 11:24:16 -05:00
Try { $ScriptBlock = [ Scriptblock ]:: Create ( $Script ) }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message ; Exit }
Write-Output "Executing: ${LivePSScript} .ps1 ${Arguments} "
2020-11-06 11:24:16 -05:00
Try { Invoke-Command -ScriptBlock $ScriptBlock -ArgumentList $Arguments }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message ; Exit }
2020-10-22 14:01:35 -04:00
}
2020-11-06 11:24:16 -05:00
'local' {
2023-10-05 13:11:38 -04:00
Write-Output "Executing: ${Path} ${Arguments} "
2020-11-06 11:24:16 -05:00
If ( $NoWait ) {
Try { Start-Process " ${Path} " -ArgumentList "{ $Arguments }" }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
2020-11-06 11:24:16 -05:00
}
Else {
2023-10-05 13:11:38 -04:00
Write-Output " Waiting for script to finish..."
2020-11-06 11:24:16 -05:00
Try { Start-Process " ${Path} " -ArgumentList "{ $Arguments }" -Wait }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
Write-Output " Finished"
2020-11-06 11:24:16 -05:00
}
2020-09-23 15:24:26 -04:00
}
}
}
2023-10-05 13:11:38 -04:00
# Return a Powershell version object from a string
2020-11-06 11:24:16 -05:00
Function Get-Version {
param ([ Parameter ( Mandatory = $true )][ string ] $Version )
[ int ] $Sets = 4
$VersionArray = $Version . Split ( '.' )
If ( $VersionArray . Count -le $Sets ) { $Sets = $VersionArray . Count }
For ( $i = 0 ; $i -le ( $Sets - 1 ); $i ++) {
$Return = '{0}.{1}' -f $Return , $VersionArray [ $i ] }
Return [ version ] $Return . Trim ( '.' )
2020-09-07 20:18:06 -04:00
}
2023-10-05 13:11:38 -04:00
# Determines whether a URL is valid
2020-11-06 11:24:16 -05:00
Function IsURLValid {
param ([ Parameter ( Mandatory = $true , ValueFromPipeline = $true )][ string ] $URL )
Try {
$HTTPRequest = [ System.Net.WebRequest ]:: Create ( $URL )
$HTTPResponse = $HTTPRequest . GetResponse ()
$HTTPStatus = [ int ] $HTTPResponse . StatusCode
$HTTPResponse . Close ()
}
Catch { }
2021-08-19 12:43:40 -04:00
If ( $HTTPStatus -ne 404 ) { Return $true }
2020-11-06 11:24:16 -05:00
Else { Return $false }
2020-09-23 21:52:25 -04:00
}
2023-10-05 13:11:38 -04:00
# Downloads a file from a URL
2020-11-06 11:24:16 -05:00
Function Download-File {
param (
[ Parameter ( Mandatory = $true , ValueFromPipeline = $true )][ string ] $URL ,
[ Parameter ( Mandatory = $false , ValueFromPipeline = $false )][ string ] $File
)
2023-10-05 12:47:08 -04:00
If ( !( $File ) ) { $File = '{0}{1}' -f ( Get-TempPath ), ( Split-Path $URL -Leaf ) }
2020-11-06 11:24:16 -05:00
If ( IsURLValid $URL ) {
If ( Test-Path $File ) {
2023-10-05 13:11:38 -04:00
Write-Output "Deleting existing file: "" ${File} """
2020-11-06 11:24:16 -05:00
Try { Remove-Item $File -Force }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
2020-11-06 11:24:16 -05:00
}
2023-10-05 13:11:38 -04:00
Write-Output "Downloading "" ${URL} "" to "" ${File} """
2020-11-06 11:24:16 -05:00
Try { ( New-Object System . Net . WebClient ). DownloadFile ( $URL , $File ) }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
2020-11-06 11:24:16 -05:00
If ( Test-Path " ${File} " ) { Return " ${File} " }
2023-10-05 13:11:38 -04:00
Else { Write-Output "Downloaded file does not exist at "" ${File} """ ; Return $false }
2020-10-19 11:24:07 -04:00
}
2023-10-05 13:11:38 -04:00
Else { Write-Output "URL is not valid or file cannot be reached: ${URL} " }
2020-10-22 14:01:35 -04:00
}
2023-10-05 13:11:38 -04:00
# Install a program from a provided path
2020-11-06 11:24:16 -05:00
Function Install-Program {
param (
[ Parameter ( Mandatory = $true , ValueFromPipeline = $true )][ string ] $Path ,
[ Parameter ( Mandatory = $false )][ string ] $Arguments = '' ,
[ Parameter ( Mandatory = $false )][ switch ] $Delete = $false
)
If ( Test-Path " ${Path} " ) {
2023-10-05 13:11:38 -04:00
Write-Output "Running installer: "" ${Path} "" ${Arguments} "
2020-12-03 17:27:53 -05:00
Try {
If ( $Arguments -eq '' ) { Start-Process " ${Path} " -Wait }
Else { Start-Process " ${Path} " -ArgumentList " ${Arguments} " -Wait }
}
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
Write-Output "Installer finished"
2020-11-06 11:24:16 -05:00
If ( $Delete ) {
2023-10-05 13:11:38 -04:00
Write-Output "Deleting installer: "" ${Path} """
2020-11-06 11:24:16 -05:00
Try { Remove-Item -Path " ${Path} " -Force }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
2020-11-06 11:24:16 -05:00
}
2020-10-22 14:01:35 -04:00
}
2023-10-05 13:11:38 -04:00
Else { Write-Output "The specified file could not be found: "" ${Path} """ }
2020-10-19 11:24:07 -04:00
}
2023-10-05 13:11:38 -04:00
# Compare current Windows version with a supplied version
# The value supplied must be a string and must include at least 1 decimal
2020-11-06 11:24:16 -05:00
Function IsWindowsVersion {
param (
[ Parameter ( Mandatory = $true , ParameterSetName = 'gt' )][ string ] $gt ,
[ Parameter ( Mandatory = $true , ParameterSetName = 'ge' )][ string ] $ge ,
[ Parameter ( Mandatory = $true , ParameterSetName = 'lt' )][ string ] $lt ,
[ Parameter ( Mandatory = $true , ParameterSetName = 'le' )][ string ] $le ,
[ Parameter ( Mandatory = $true , ParameterSetName = 'eq' )][ string ] $eq
)
[ version ] $WindowsVersion = [ System.Environment ]:: OSVersion . Version
switch ( $PSCmdlet . ParameterSetName ) {
'gt' { If ( $WindowsVersion -gt ( Get-Version $gt ) ) { Return $true } Else { Return $false } }
'ge' { If ( $WindowsVersion -ge ( Get-Version $ge ) ) { Return $true } Else { Return $false } }
'lt' { If ( $WindowsVersion -lt ( Get-Version $lt ) ) { Return $true } Else { Return $false } }
'le' { If ( $WindowsVersion -le ( Get-Version $le ) ) { Return $true } Else { Return $false } }
'eq' { If ( $WindowsVersion -eq ( Get-Version $eq ) ) { Return $true } Else { Return $false } }
}
2020-09-09 16:46:21 -04:00
}
2023-10-05 13:11:38 -04:00
# Compare current Windows build with a supplied version
# The value supplied must be an int
2020-11-06 11:24:16 -05:00
Function IsWindowsBuild {
param (
[ Parameter ( Mandatory = $true , ParameterSetName = 'gt' )][ int ] $gt ,
[ Parameter ( Mandatory = $true , ParameterSetName = 'ge' )][ int ] $ge ,
[ Parameter ( Mandatory = $true , ParameterSetName = 'lt' )][ int ] $lt ,
[ Parameter ( Mandatory = $true , ParameterSetName = 'le' )][ int ] $le ,
[ Parameter ( Mandatory = $true , ParameterSetName = 'eq' )][ int ] $eq
)
[ int ] $WindowsBuild = [ System.Environment ]:: OSVersion . Version . Build
switch ( $PSCmdlet . ParameterSetName ) {
'gt' { If ( $WindowsBuild -gt $gt ) { Return $true } Else { Return $false } }
'ge' { If ( $WindowsBuild -ge $ge ) { Return $true } Else { Return $false } }
'lt' { If ( $WindowsBuild -lt $lt ) { Return $true } Else { Return $false } }
'le' { If ( $WindowsBuild -le $le ) { Return $true } Else { Return $false } }
'eq' { If ( $WindowsBuild -eq $eq ) { Return $true } Else { Return $false } }
}
2020-09-25 13:13:03 -04:00
}
2023-10-05 13:11:38 -04:00
# Get the path to the TEMP folder (context dependent)
2023-10-05 12:47:08 -04:00
Function Get-TempPath { Return [ System.IO.Path ]:: GetTempPath () }
2020-10-22 14:01:35 -04:00
2023-10-05 13:11:38 -04:00
# Determine if the system is 64-bit or not
2020-11-06 11:24:16 -05:00
Function Is64bit {
switch ( $ ( Get-CimInstance -ClassName Win32_OperatingSystem -Property OSArchitecture | Select-Object -ExpandProperty OSArchitecture ) )
{ '64-bit' { Return $true } '32-bit' { Return $false } }
2020-09-25 13:13:03 -04:00
}
2023-10-05 13:11:38 -04:00
# Determine if the Powershell process is 64-bit or not
2020-11-06 11:24:16 -05:00
Function Is64bitShell { If ( [ System.Environment ]:: Is64BitProcess ) { Return $true } Else { Return $false } }
2020-10-19 11:24:07 -04:00
2023-10-05 13:11:38 -04:00
# Determine if a user is in a built-in role or specified group
2020-10-22 14:01:35 -04:00
Function IsMemberOf {
2020-11-06 11:24:16 -05:00
param (
[ Parameter ( Mandatory = $false )]
[ switch ] $Builtin = $false ,
[ Parameter ( Mandatory = $true , ValueFromPipeline = $true )]
[ string ] $Group
)
If ( $Builtin ) {
Switch ( $Group ) {
( "Administrator" -or "Administrators" )
{ $Group = [ System.Security.Principal.WindowsBuiltInRole ]:: Administrator ; Break }
( "Backup Operator" -or "Backup Operators" -or "BackupOperator" -or "BackupOperators" )
{ $Group = [ System.Security.Principal.WindowsBuiltInRole ]:: BackupOperator ; Break }
( "System Operator" -or "System Operators" -or "SystemOperator" -or "SystemOperators" )
{ $Group = [ System.Security.Principal.WindowsBuiltInRole ]:: SystemOperator ; Break }
( "Account Operator" -or "Account Operators" -or "AccountOperator" -or "AccountOperators" )
{ $Group = [ System.Security.Principal.WindowsBuiltInRole ]:: AccountOperator ; Break }
( "Print Operator" -or "Print Operators" -or "PrintOperator" -or "PrintOperators" )
{ $Group = [ System.Security.Principal.WindowsBuiltInRole ]:: PrintOperator ; Break }
( "Replicator" -or "Replicators" )
{ $Group = [ System.Security.Principal.WindowsBuiltInRole ]:: Replicator ; Break }
( "Power User" -or "Power Users" -or "PowerUser" -or "PowerUsers" )
{ $Group = [ System.Security.Principal.WindowsBuiltInRole ]:: PowerUser ; Break }
( "User" -or "Users" )
{ $Group = [ System.Security.Principal.WindowsBuiltInRole ]:: User ; Break }
( "Guest" -or "Guests" )
{ $Group = [ System.Security.Principal.WindowsBuiltInRole ]:: Guest ; Break }
default { ; Break }
}
2020-10-22 14:01:35 -04:00
}
2020-11-06 11:24:16 -05:00
Return ([ Security.Principal.WindowsPrincipal][System.Security.Principal.WindowsIdentity ]:: GetCurrent ()). IsInRole ( $Group )
2020-10-22 14:01:35 -04:00
}
2020-11-06 11:24:16 -05:00
2023-10-05 13:11:38 -04:00
# Start, stop, restart, or delete a list of services
2020-11-06 11:24:16 -05:00
Function Control-Service
{
param (
[ Parameter ( Mandatory = $true , ParameterSetName = 'start' )]
[ switch ] $Start ,
[ Parameter ( Mandatory = $true , ParameterSetName = 'stop' )]
[ switch ] $Stop ,
[ Parameter ( Mandatory = $true , ParameterSetName = 'restart' )]
[ switch ] $Restart ,
2021-07-29 12:57:11 -04:00
[ Parameter ( Mandatory = $true , ParameterSetName = 'delete' )]
[ switch ] $Delete ,
2021-10-27 13:11:19 -04:00
[ Parameter ( Mandatory = $true , ParameterSetName = 'disable' )]
[ switch ] $Disable ,
2020-11-06 11:24:16 -05:00
[ Parameter ( Mandatory = $true , ValueFromPipeline = $true , ParameterSetName = 'start' )]
[ Parameter ( Mandatory = $true , ValueFromPipeline = $true , ParameterSetName = 'stop' )]
[ Parameter ( Mandatory = $true , ValueFromPipeline = $true , ParameterSetName = 'restart' )]
2021-07-29 12:57:11 -04:00
[ Parameter ( Mandatory = $true , ValueFromPipeline = $true , ParameterSetName = 'delete' )]
2021-10-27 13:11:19 -04:00
[ Parameter ( Mandatory = $true , ValueFromPipeline = $true , ParameterSetName = 'disable' )]
2020-11-06 11:24:16 -05:00
[ string[] ] $Name ,
[ Parameter ( Mandatory = $false , ParameterSetName = 'start' )]
[ Parameter ( Mandatory = $false , ParameterSetName = 'stop' )]
[ Parameter ( Mandatory = $false , ParameterSetName = 'restart' )]
2021-07-29 12:57:11 -04:00
[ Parameter ( Mandatory = $false , ParameterSetName = 'delete' )]
2021-10-27 13:11:19 -04:00
[ Parameter ( Mandatory = $false , ParameterSetName = 'disable' )]
2020-11-06 11:24:16 -05:00
[ switch ] $Match = $false
)
switch ( $PSCmdlet . ParameterSetName ) {
'start' {
$statuscondition = 'Running'
$verb = 'start'
$verbing = 'Starting'
}
'stop' {
$statuscondition = 'Stopped'
$verb = 'stop'
$verbing = 'Stopping'
}
'restart' {
$ArgString = $MyInvocation . Line
$StopCmd = $ArgString -replace ' -Restart ' , ' -Stop '
$StartCmd = $ArgString -replace ' -Restart ' , ' -Start '
Invoke-Expression $StopCmd
Invoke-Expression $StartCmd
Exit
}
2021-07-29 12:57:11 -04:00
'delete' {
2023-10-05 13:11:38 -04:00
# Delete the service
2021-07-29 12:57:11 -04:00
}
2021-10-27 13:11:19 -04:00
'disable' {
$ArgString = $MyInvocation . Line
$StopCmd = $ArgString -replace ' -Disable ' , ' -Stop '
Invoke-Expression $StopCmd
$statuscondition = 'Disabled'
$verb = 'disable'
$verbing = 'Disabling'
}
2020-11-06 11:24:16 -05:00
}
ForEach ( $item in $Name ) {
$service = Get-Service -Name $item -ErrorAction SilentlyContinue
If ( !( $service ) ) {
2023-10-05 13:11:38 -04:00
Write-Output "Cannot find the service ' ${item} '"
2020-11-06 11:24:16 -05:00
If ( $Match ) {
2023-10-05 13:11:38 -04:00
Write-Output "Attempting to match ' ${item} ' to service display names"
2021-10-27 13:11:19 -04:00
ForEach ( $svc in (( Get-Service | Where-Object { $_ . DisplayName -match $item }). Name ) ) {
2020-11-06 11:24:16 -05:00
Switch ( $verb ) {
'start' { Control-Service -Start -Name $svc }
'stop' { Control-Service -Stop -Name $svc }
2021-10-27 13:11:19 -04:00
'disable' { Control-Service -Disable -Name $svc }
2020-11-06 11:24:16 -05:00
}
}
}
}
Else {
$name = $service . Name
$fullname = $service . DisplayName
2021-10-27 13:11:19 -04:00
If ( ([ string ]:: IsNullOrEmpty ( $fullname )) -or ( $fullname -eq $name ) ) {
2020-11-06 11:24:16 -05:00
$displayname = "' ${name} '"
}
Else {
$displayname = """ ${fullname} "" ( ${name} )"
}
If ( $service . Status -ne $statuscondition ) {
2023-10-05 13:11:38 -04:00
Write-Output " ${verbing} ${displayname} "
2021-10-27 13:11:19 -04:00
Try {
Switch ( $verb ) {
'start' { Start-Service -Name $name }
'stop' { Stop-Service -Name $name -Force }
'disable' { Set-Service -Name " ${name} " -StartupType Disabled }
2020-11-06 11:24:16 -05:00
}
}
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
2020-11-06 11:24:16 -05:00
}
}
}
}
2023-10-05 13:11:38 -04:00
# Stop a process
2020-11-06 11:24:16 -05:00
Function End-Process
{
param (
[ Parameter ( Mandatory = $true , ValueFromPipeline = $true )][ string[] ] $Name ,
[ Parameter ( Mandatory = $false )][ switch ] $Match = $false ,
2021-07-30 09:42:54 -04:00
[ Parameter ( Mandatory = $false )][ switch ] $Force = $false
2020-11-06 11:24:16 -05:00
)
ForEach ( $item in $Name ) {
$process = Get-Process -Name $item -ErrorAction SilentlyContinue
If ( !( $process ) ) {
2023-10-05 13:11:38 -04:00
Write-Output "Cannot find the process ' ${item} '"
2020-11-06 11:24:16 -05:00
If ( $Match ) {
2023-10-05 13:11:38 -04:00
Write-Output "Attempting to match ' ${item} ' to all running process names"
2023-08-23 14:50:47 -04:00
ForEach ( $proc in (( Get-Process | Where-Object { $_ . Name -match $item }). Name ) ) {
2020-11-06 11:24:16 -05:00
If ( $Force ) { End-Process -Name $proc -Force } Else { End-Process -Name $proc }
}
}
}
Else {
$name = $process . Name
If ( $Force ) {
2023-10-05 13:11:38 -04:00
Write-Output "Forcefully stopping ' ${name} ' process"
2020-11-06 11:24:16 -05:00
Stop-Process $process -Force -ErrorAction SilentlyContinue
}
Else {
2023-10-05 13:11:38 -04:00
Write-Output "Attempting to gracefully close ' ${name} ' process"
2020-11-06 11:24:16 -05:00
$process . CloseMainWindow () | Out-Null
}
}
}
2020-10-22 14:01:35 -04:00
}
2020-12-03 18:16:55 -05:00
Function Create-Shortcut {
param (
[ Parameter ( Mandatory = $true )][ string ] $Path ,
[ Parameter ( Mandatory = $true )][ string ] $TargetPath ,
[ Parameter ( Mandatory = $false )][ string ] $Arguments ,
[ Parameter ( Mandatory = $false )][ string ] $Description ,
[ Parameter ( Mandatory = $false )][ string ] $Icon = '' ,
[ Parameter ( Mandatory = $false )][ string ] $WorkingDirectory = ''
)
If ( Test-Path $TargetPath ) {
If ( Test-Path $Path ) {
2023-10-05 13:11:38 -04:00
Write-Output "Deleting existing shortcut at "" ${Path} """
2020-12-03 18:16:55 -05:00
Try { Remove-Item $Path -Force -ErrorAction SilentlyContinue }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
2020-12-03 18:16:55 -05:00
}
If ( $WorkingDirectory -eq '' ) { $WorkingDirectory = Split-Path $TargetPath -Parent }
If ( $Icon -eq '' ) { $Icon = " ${TargetPath} , 0" }
$WshShell = New-Object -ComObject WScript . Shell
$Shortcut = $WshShell . CreateShortcut ( $Path )
$Shortcut . Arguments = $Arguments
$Shortcut . Description = $Description
$Shortcut . IconLocation = $Icon
$Shortcut . WorkingDirectory = $WorkingDirectory
$Shortcut . TargetPath = $TargetPath
2023-10-05 13:11:38 -04:00
Write-Output "Creating shortcut to "" ${TargetPath} "" at "" ${Path} """
2020-12-03 18:16:55 -05:00
Try { $Shortcut . Save () }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
2020-12-03 18:16:55 -05:00
}
2023-10-05 13:11:38 -04:00
Else { Write-Output "Cannot create shortcut because the target path does not exist" }
2021-05-24 14:05:59 -04:00
}
# Function to enable or disable a specified log
2022-12-15 12:03:47 -05:00
Function Toggle-Log {
2021-05-24 14:05:59 -04:00
param (
[ Parameter ( Mandatory = $true )][ string ] $Name ,
[ Parameter ( Mandatory = $true , ParameterSetName = 'Enable' )][ switch ] $Enable ,
[ Parameter ( Mandatory = $true , ParameterSetName = 'Disable' )][ switch ] $Disable
)
Try {
$log = New-Object System . Diagnostics . Eventing . Reader . EventLogConfiguration $Name
2023-10-05 13:11:38 -04:00
If ( $Enable ) { Write-Output "Enabing ${Name} event log" ; $log . IsEnabled = $true }
If ( $Disable ) { Write-Output "Disabing ${Name} event log" ; $log . IsEnabled = $false }
2021-05-24 14:05:59 -04:00
$log . SaveChanges ()
}
2023-10-05 13:11:38 -04:00
Catch { Write-Output $_ . Exception . Message }
2021-08-05 09:42:15 -04:00
}
2021-09-23 13:14:16 -04:00
# Function to install the PSAtera Powershell module
2022-12-15 12:03:47 -05:00
Function Install-PSAteraModule {
2021-09-23 13:14:16 -04:00
Try {
$module = Get-Module -ListAvailable -Name PSAtera
If ( ( -not $module ) -and ( IsAdmin ) ) {
2023-10-05 13:11:38 -04:00
Write-Output "Installing PSAtera Powershell module"
2021-10-04 17:03:54 -04:00
Install-Module -Name PSAtera -Force
2021-09-23 13:14:16 -04:00
}
ElseIf ( ( -not $module ) -and ( -not ( IsAdmin )) ) {
2023-10-05 13:11:38 -04:00
Write-Output "The PSAtera module needs to be installed, but the current security context is not sufficient to install it"
2021-09-23 13:14:16 -04:00
}
}
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
2021-09-23 13:14:16 -04:00
}
# Function to install the MSP360 Powershell module
2022-12-15 12:03:47 -05:00
Function Install-MSP360Module {
2021-09-23 13:14:16 -04:00
Try {
$module = Get-Module -ListAvailable -Name MSP360
If ( ( -not $module ) -and ( IsAdmin ) ) {
2023-10-05 13:11:38 -04:00
Write-Output "Installing MSP360 Powershell module"
2021-09-23 13:14:16 -04:00
Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process
[ Net.ServicePointManager ]:: SecurityProtocol = [ Net.SecurityProtocolType ]:: Tls12 ; Invoke-Expression ( New-Object System . Net . WebClient ). DownloadString ( 'https://git.io/JUSAA' ); Install-MSP360Module
}
ElseIf ( ( -not $module ) -and ( -not ( IsAdmin )) ) {
2023-10-05 13:11:38 -04:00
Write-Output "The MSP360 Powershell module needs to be installed, but the current security context is not sufficient to install it"
2021-09-23 13:14:16 -04:00
}
}
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
2021-09-23 13:14:16 -04:00
}
2021-10-08 05:42:05 -04:00
# Function to delete a file
2022-12-15 12:03:47 -05:00
Function Remove-File ([ Parameter ( ValueFromPipeline = $true )][ string ] $path ) {
2021-10-08 05:42:05 -04:00
If ( Test-Path $path ) {
If ( !(( Get-Item $path ) -is [ System.IO.DirectoryInfo ]) ) {
Try {
2023-10-05 13:11:38 -04:00
Write-Output "Delete "" ${path} """
2021-10-08 05:42:05 -04:00
Remove-Item $path -Force
}
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
2021-10-08 05:42:05 -04:00
}
}
}
2021-11-10 20:18:43 -05:00
# Function to ZIP a file or directory
2022-12-15 12:03:47 -05:00
Function Zip-Object {
2021-11-12 11:09:52 -05:00
param (
[ Parameter ( ValueFromPipeline = $true )][ string ] $path ,
[ Parameter ( ValueFromPipeline = $false )][ string ] $dest
)
2021-11-10 20:18:43 -05:00
If ( $ ( Get-Item $path ) -is [ System.IO.DirectoryInfo ] ) {
2021-11-12 11:09:52 -05:00
If ( [ string ]:: IsNullOrEmpty ( $dest ) ) { $dest = '{0}\{1}.zip' -f $ ( Split-Path -Parent $path ), $ ( Split-Path -Leaf $path ) }
2023-10-05 13:11:38 -04:00
Write-Output "Creating zip file of: ${path} "
2021-11-10 20:18:43 -05:00
Try {
Add-Type -Assembly "System.IO.Compression.FileSystem"
[ IO.Compression.ZipFile ]:: CreateFromDirectory ( $path , $dest )
}
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
2021-11-10 20:18:43 -05:00
If ( Test-Path $dest ) { Return $dest } Else { Return $false }
}
ElseIf ( $ ( Get-Item $path ) -is [ System.IO.FileInfo ] ) {
$dir = $path . Substring ( 0 , $path . LastIndexOf ( '.' ))
Try {
2023-10-05 13:11:38 -04:00
Write-Output "Creating directory: ${path} "
2021-11-10 20:18:43 -05:00
New-Item -ItemType Directory -Path $dir -ErrorAction Stop | Out-Null
2023-10-05 13:11:38 -04:00
Write-Output "Moving "" ${path} "" into "" ${dir} """
2021-11-10 20:18:43 -05:00
Move-Item -Path $path -Destination $dir -Force -ErrorAction Stop | Out-Null
}
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
2021-11-10 20:18:43 -05:00
Zip-Object $dir
}
2023-10-05 13:11:38 -04:00
Else { Write-Output "Could not determine the file/folder status: ${path} " }
2021-11-10 20:18:43 -05:00
}
# Function to UNZIP a file or directory
2022-12-15 12:03:47 -05:00
Function Unzip-Object ([ Parameter ( ValueFromPipeline = $true )][ string ] $path ) {
2021-11-12 11:09:52 -05:00
If ( [ System.IO.Path ]:: GetExtension ( $path ) -eq ".zip" ) {
2021-11-10 20:18:43 -05:00
$dest = Split-Path -Parent $path
2023-10-05 13:11:38 -04:00
Write-Output "Extracting "" ${path} "" to "" ${dest} """
2021-11-10 20:18:43 -05:00
Try {
Add-Type -Assembly "System.IO.Compression.FileSystem"
[ IO.Compression.ZipFile ]:: ExtractToDirectory ( $path , $dest )
}
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
2021-11-10 20:18:43 -05:00
}
2023-10-05 13:11:38 -04:00
Else { Write-Output "The path specified is not a zip file: ${path} " }
2021-11-10 20:18:43 -05:00
}
2021-11-18 21:03:03 -05:00
# Function to get a copy of the LiquidFiles CLI agent
2022-12-15 12:03:47 -05:00
Function Get-LiquidFilesCLI {
2021-11-18 21:03:03 -05:00
$url = 'https://lfupdate.s3.amazonaws.com/clients/windows_cli/LiquidFilesCLI-2.0.128.zip'
$zip_filename = Split-Path -Leaf $url
$zip_path = " ${Global:ToolsDirectory} \ ${zip_filename} "
$exe_path = '{0}\{1}.exe' -f $Global:ToolsDirectory ,[ System.IO.Path ]:: GetFileNameWithoutExtension ( $zip_filename )
If ( Test-Path $zip_path ) {
2023-10-05 13:11:38 -04:00
Write-Output "Deleting existing zip archive: ${zip_path} "
2021-11-18 21:03:03 -05:00
Try { Remove-Item -Path $zip_path -Force }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
2021-11-18 21:03:03 -05:00
}
If ( Test-Path $exe_path ) {
2023-10-05 13:11:38 -04:00
Write-Output "Deleting existing file: ${exe_path} "
2021-11-18 21:03:03 -05:00
Try { Remove-Item -Path $exe_path -Force }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
2021-11-18 21:03:03 -05:00
}
Download-File -URL $url -File $zip_path | Out-Null
Unzip-Object $zip_path
Return $exe_path
}
2022-02-03 09:20:26 -05:00
# Function to determine if an app is running
2022-12-15 12:03:47 -05:00
Function IsRunning ([ Parameter ( ValueFromPipeline = $true )][ string ] $Name ) {
2022-11-19 14:38:02 -05:00
$RunningInstances = Get-Process | Where-Object { $_ . Name -ilike $Name }
2022-02-03 09:20:26 -05:00
If ( $RunningInstances ) { Return $true } Else { Return $false }
}
2022-03-17 18:09:47 -04:00
# Function to create a local user account
2022-12-15 12:03:47 -05:00
Function Create-LocalUser {
2022-03-17 18:09:47 -04:00
param (
[ Parameter ( Mandatory = $true )][ string ] $Username ,
[ Parameter ( Mandatory = $true )][ string ] $Password ,
[ Parameter ( Mandatory = $true )][ string ] $FullName ,
[ Parameter ( Mandatory = $true )][ string ] $Description ,
[ Parameter ( Mandatory = $false )][ switch ] $MakeAdmin = $false ,
[ Parameter ( Mandatory = $false )][ switch ] $Hide = $false
)
2023-10-05 13:11:38 -04:00
# Secure supplied password
2022-03-17 18:09:47 -04:00
Try { $SecurePassword = ConvertTo-SecureString $Password -AsPlainText -Force ; $Password = $null }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message ; Exit }
2022-03-17 18:09:47 -04:00
If ( IsAdmin )
{
If ( $ ( Get-CimInstance -ClassName Win32_OperatingSystem -Property ProductType | Select-Object -ExpandProperty ProductType ) -ne "2" )
{
If ( $ ( Get-LocalUser -Name $Username -ErrorAction SilentlyContinue ) )
{
2023-10-05 13:11:38 -04:00
Write-Output "Updating user: ${Username} "
2022-03-17 18:09:47 -04:00
Try { Set-LocalUser -Name $Username -Password $SecurePassword -FullName $FullName -Description $Description -AccountNeverExpires -PasswordNeverExpires }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message ; Exit }
2022-03-17 18:09:47 -04:00
}
Else
{
2023-10-05 13:11:38 -04:00
Write-Output "Creating user: ${Username} "
2022-03-17 18:09:47 -04:00
Try { New-LocalUser -Name $Username -Password $SecurePassword -FullName $FullName -Description $Description -AccountNeverExpires -PasswordNeverExpires }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message ; Exit }
2022-03-17 18:09:47 -04:00
}
2022-03-17 21:27:22 -04:00
If ( ( -not ( $ ( Get-LocalGroupMember -Group "Administrators" -Member $Username -ErrorAction SilentlyContinue ))) -and $MakeAdmin )
2022-03-17 18:09:47 -04:00
{
2023-10-05 13:11:38 -04:00
Write-Output "Adding "" ${Username} "" to local Administrators group"
2022-03-17 21:27:22 -04:00
Try { Add-LocalGroupMember -Name "Administrators" -Member $ ( Get-LocalUser -Name $Username ) }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message ; Exit }
2022-03-17 18:09:47 -04:00
}
}
2023-10-05 13:11:38 -04:00
Else { Write-Output "Cannot create or modify local accounts on a domain controller" }
2022-03-17 18:09:47 -04:00
}
2023-10-05 13:11:38 -04:00
Else { Write-Output "Cannot create or modify a local account without administrative privileges" }
2022-03-17 18:09:47 -04:00
2022-03-24 16:28:30 -04:00
If ( $Hide ) {
# TODO: Hide account from logon screen
#Get-ChildItem "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList"
}
}
# Returns the number of MB a process is consuming
2022-12-15 12:03:47 -05:00
Function Get-MemoryUsage ([ string ] $AppName ) {
2022-03-24 16:28:30 -04:00
[ math ]:: Round (( Get-Process -Name $AppName -ErrorAction SilentlyContinue | Measure-Object -Property WorkingSet -Sum ). Sum / 1 MB)
}
# Kills a process if it's using more than the specified amount of memory
2022-12-15 12:03:47 -05:00
Function Stop-MemoryHog ([ string ] $AppName ,[ int ] $MemoryLimit ) {
2022-03-24 16:28:30 -04:00
$MemoryUsed = ( Get-MemoryUsage -AppName $AppName )
If ( $MemoryUsed -gt $MemoryLimit ) {
$MemoryDifference = $MemoryUsed - $MemoryLimit
2023-10-05 13:11:38 -04:00
Write-Output "Killing "" ${AppName} "" for using ${MemoryDifference} MB over the limit of ${MemoryLimit} MB"
2022-03-24 16:28:30 -04:00
Try { Get-Process -Name $AppName -ErrorAction SilentlyContinue | Stop-Process -Force }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
2022-03-24 16:28:30 -04:00
}
2022-03-17 18:09:47 -04:00
}
2022-11-10 11:22:43 -05:00
# Returns a formatted list of the largest or smallest files in a directory
2022-12-15 12:03:47 -05:00
Function Get-FileSizes {
2022-11-10 11:22:43 -05:00
param (
[ Parameter ( ValueFromPipeline = $true )][ string ] $Path ,
[ switch ] $Recurse ,
[ switch ] $IncludeHidden ,
[ Parameter ( ParameterSetName = "largest" )][ int ] $Largest ,
[ Parameter ( ParameterSetName = "smallest" )][ int ] $Smallest ,
[ string ] $Units ,
[ int ] $Precision = 2
)
# If no path is specified, set $Path to the directory containing user profiles
If ( -not $Path ) {
$Path = ( Get-ItemPropertyValue -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' -Name ProfilesDirectory )
$Recurse = $true
$IncludeHidden = $false
}
# Change the sort order depending on which parameter set was specified
Switch ( $PSCmdlet . ParameterSetName ) {
"largest" { $SortOrder = $true ; $Quantity = $Largest }
"smallest" { $SortOrder = $false ; $Quantity = $Smallest }
}
# Set the units to show sizes in output
Switch ( $Units . ToUpper () ) {
"KB" { $UnitLabel = "Kilobytes (KB)" ; $Divisor = 1 KB }
"MB" { $UnitLabel = "Megabytes (MB)" ; $Divisor = 1 MB }
"GB" { $UnitLabel = "Gigabytes (GB)" ; $Divisor = 1 GB }
"TB" { $UnitLabel = "Terabytes (TB)" ; $Divisor = 1 TB }
default { $UnitLabel = "Bytes" ; $Divisor = 1 }
}
# Get the file paths and sizes as specified
$FileSizes = Get-ChildItem $Path -Recurse: $Recurse -Force: $IncludeHidden -ErrorAction SilentlyContinue |
Sort-Object -Descending: $SortOrder -Property Length |
Select-Object -First $Quantity @ { Name = "File Path" ; Expression ={ $_ . DirectoryName + '\' + $_ . Name }}, @ { Name = $UnitLabel ; Expression ={[ Math ]:: Round ( $_ . Length / $Divisor , $Precision )}}
# Set output prefix
$prefix = "The ${Quantity} " + $PSCmdlet . ParameterSetName + " files found at "" ${Path} """
# Output to console
$constr = " ${prefix} " + $ ( $FileSizes | Out-String )
Write-Output $constr
# Output to log file
$logstr = " ${prefix} " + $ ( $FileSizes | Format-List | Out-String )
2023-10-05 13:11:38 -04:00
Write-Output $logstr
2022-11-10 11:22:43 -05:00
}
2022-11-11 15:11:14 -05:00
# Function to write text to a file in UTF8 encoding without BOM
2022-12-15 12:03:47 -05:00
Function WriteToFile_UTF8NoBOM {
2022-11-11 15:11:14 -05:00
param (
[ Parameter ( ValueFromPipeline = $false )][ string ] $FilePath ,
[ Parameter ( ValueFromPipeline = $true )][ string ] $Value
)
$UTF8NoBOM = New-Object System . Text . UTF8Encoding $false
Try { [ IO.File ]:: WriteAllText ( $FilePath , $CONFIG , $UTF8NoBOM ) }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
2022-11-11 15:11:14 -05:00
}
2022-11-19 14:38:02 -05:00
# Function to enumerate all user profile folders
2022-12-15 12:03:47 -05:00
Function EnumUserProfiles {
2022-11-19 14:38:02 -05:00
$ProfilePath = ( Get-ItemPropertyValue -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' -Name ProfilesDirectory )
$UserProfilePaths = @ ()
ForEach ( $userProfile in ( Get-ChildItem -Path $ProfilePath | Where-Object { $_ . PSIsContainer }) ) {
$UserProfilePaths += , @ ( $userProfile . FullName )
}
Return $UserProfilePaths
}
2022-11-23 15:59:33 -05:00
# Function to test for the presense of a Registry property
2022-12-15 12:03:47 -05:00
Function Test-RegistryProperty {
2022-11-23 15:59:33 -05:00
param (
[ Parameter ( Mandatory = $true )][ string ] $Path ,
[ Parameter ( Mandatory = $true )][ string ] $Name
)
Try {
Get-ItemProperty -Path $Path | Select-Object -ExpandProperty $Name -ErrorAction Stop | Out-Null
Return $true
}
Catch { Return $false }
}
2022-12-15 12:03:47 -05:00
# Function to format a quantity of bytes into a denomination
Function Beautify-Bytes {
param (
[ int64 ] $Amount ,
[ int ] $Precision = 2 ,
[ string ] $Units ,
[ switch ] $WithUnitLabel
)
Switch ( $Units . ToUpper () ) {
"KB" { $UnitLabel = "KB" ; $Divisor = 1 KB }
"MB" { $UnitLabel = "MB" ; $Divisor = 1 MB }
"GB" { $UnitLabel = "GB" ; $Divisor = 1 GB }
"TB" { $UnitLabel = "TB" ; $Divisor = 1 TB }
default { $UnitLabel = "bytes" ; $Divisor = 1 }
}
Switch ( $WithUnitLabel ) {
$true { Return ( ZeroPad-Decimal -Amount $ ([ Math ]:: Round ( $Amount / $Divisor , $Precision )) -NeededDecimalPlaces $Precision ) + " ${UnitLabel} " }
$false { Return ( ZeroPad-Decimal -Amount $ ([ Math ]:: Round ( $Amount / $Divisor , $Precision )) -NeededDecimalPlaces $Precision ) }
}
}
# Function to pad zeros onto the end of a decimal number
Function ZeroPad-Decimal {
param (
$Amount ,
$NeededDecimalPlaces
)
$splitNumbers = $Amount . ToString (). Split ( '.' )
$paddedDecimal = $splitNumbers [ 1 ]. PadRight ( $NeededDecimalPlaces , '0' )
Return $splitNumbers [ 0 ] + '.' + $paddedDecimal
}
2023-01-04 10:42:37 -05:00
# Function to follow a URL and return the absolute URI of the result (whether redirected or not)
Function Get-AbsoluteURI {
param ([ string ] $URL )
Return [ System.Net.HttpWebRequest ]:: Create ( $URL ). GetResponse (). ResponseUri . AbsoluteUri
}
2023-08-22 16:09:13 -04:00
Function Get-GUIDFromMSIUninstallString ([ string ] $str ) {
$start = $str . IndexOf ( '{' ) + 1
$end = $str . IndexOf ( '}' )
2023-08-23 15:01:36 -04:00
$retval = $str . Substring ( $start , $end - $start )
If ( $retval . Length -ne 36 ) { $retval = $null }
Return $retval
2023-08-22 16:09:13 -04:00
}
2023-08-23 13:56:04 -04:00
Function Uninstall-MSI ([ Parameter ( ValueFromPipeline = $true )][ string[] ] $APP_NAMES ) {
PROCESS {
# Get all of the uninstall registry keys
2023-08-23 15:10:45 -04:00
$UNINSTALL_KEYS = Get-ChildItem -Path HKLM : \ SOFTWARE \ Microsoft \ Windows \ CurrentVersion \ Uninstall -ErrorAction SilentlyContinue
$UNINSTALL_KEYS += Get-ChildItem -Path HKLM : \ SOFTWARE \ WOW6432Node \ Microsoft \ Windows \ CurrentVersion \ Uninstall -ErrorAction SilentlyContinue
# Exit if we can't find any software at all
2023-10-05 13:11:38 -04:00
If ( $UNINSTALL_KEYS . Length -eq 0 ) { Write-Error "Attempted to uninstall ${APP_NAMES} but could not find any installed apps in the Windows Registry" ; Return }
2023-08-22 16:09:13 -04:00
2023-08-23 13:56:04 -04:00
# Array to store custom objects about each installed MSI app
$INSTALLED_APPS = @ ()
2023-08-22 16:09:13 -04:00
2023-08-23 13:56:04 -04:00
Foreach ( $reg in $UNINSTALL_KEYS ) {
# Get the uninstall key
$REG_PROPERTY = Get-ItemProperty -Path $reg . PSPath
2023-08-22 16:09:13 -04:00
2023-08-23 13:56:04 -04:00
# Guards to prevent working with incompatible apps
If ( [ string ]:: IsNullOrEmpty ( $REG_PROPERTY . UninstallString ) -and [ string ]:: IsNullOrEmpty ( $REG_PROPERTY . ModifyPath ) ) { Continue }
2023-08-22 16:09:13 -04:00
2023-08-23 13:56:04 -04:00
# Determine if the app is installed via MSI
If ( $REG_PROPERTY . WindowsInstaller -eq 1 ) { $UNINSTALL_SUPPORTED = $true } Else { $UNINSTALL_SUPPORTED = $false }
2023-08-22 16:09:13 -04:00
2023-08-23 13:56:04 -04:00
# Get the product code from the UninstallString or ModifyPath if the app is installed via MSI
[ string ] $PRODUCT_CODE = $null
If ( ![ string ]:: IsNullOrEmpty ( $REG_PROPERTY . UninstallString ) -and $UNINSTALL_SUPPORTED ) { $PRODUCT_CODE = $ ( Get-GUIDFromMSIUninstallString -str $REG_PROPERTY . UninstallString ) }
2023-08-22 16:09:13 -04:00
2023-08-23 13:56:04 -04:00
# Get the product code from the ModifyPath
ElseIf ( ![ string ]:: IsNullOrEmpty ( $REG_PROPERTY . ModifyPath ) -and $UNINSTALL_SUPPORTED ) { $PRODUCT_CODE = $ ( Get-GUIDFromMSIUninstallString -str $REG_PROPERTY . ModifyPath ) }
2023-08-22 16:09:13 -04:00
2023-08-23 13:56:04 -04:00
# Additional guard to prevent adding an app with no product code
If ( [ string ]:: IsNullOrEmpty ( $PRODUCT_CODE ) ) { Continue }
2023-08-22 16:09:13 -04:00
2023-08-23 13:56:04 -04:00
$customObject = [ PSCustomObject ] @ {
UninstallSupported = $UNINSTALL_SUPPORTED
DisplayName = $REG_PROPERTY . DisplayName
UninstallString = $REG_PROPERTY . UninstallString
ModifyPath = $REG_PROPERTY . ModifyPath
ProductCode = $PRODUCT_CODE
}
2023-08-22 16:09:13 -04:00
2023-08-23 13:56:04 -04:00
$INSTALLED_APPS += $customObject
}
Foreach ( $app in $APP_NAMES ) {
Foreach ( $installed_app in $INSTALLED_APPS ) {
# Make sure the DisplayName matches what we're looking for
If ( !( $installed_app . DisplayName -ilike $app ) ) { Continue }
$DisplayName = $installed_app . DisplayName
$UninstallString = $installed_app . UninstallString
$ProductCode = $installed_app . ProductCode
# Print out uninstall string for unsupported apps
If ( $installed_app . UninstallSupported -eq $false ) {
2023-10-05 13:11:38 -04:00
Write-Output "Cannot uninstall "" ${DisplayName} "" `n UninstallString: ${UninstallString} "
2023-08-23 13:56:04 -04:00
Continue
2023-08-22 16:09:13 -04:00
}
2023-08-23 13:56:04 -04:00
# Uninstall the app
2023-10-05 13:11:38 -04:00
Write-Output "Uninstalling "" ${DisplayName} "" with product code: ${ProductCode} "
2023-08-23 13:56:04 -04:00
$arguments = '/X{' + $ProductCode + '} /qn /norestart'
Try { Start-Process -FilePath "msiexec.exe" -ArgumentList $arguments -Wait }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
2023-08-22 16:09:13 -04:00
}
}
}
}
2023-09-07 10:59:55 -04:00
# Reboot an endpoint based on the amount of time it has been running without a reboot
Function Restart-EndpointOnUptime ([ timespan ] $MaxUptime ) {
# Get the length of time the endpoint has been running without restart
Try { $LastBootTime = ( Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop ). LastBootUpTime }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message ; Exit }
2023-09-07 10:59:55 -04:00
# Get the difference between the endpoint uptime and the current time
$Delta = New-TimeSpan -Start $LastBootTime -End $ ( Get-Date )
# Stop the function if the endpoint has not exceeded $MaxUptime
If ( $Delta -lt $MaxUptime ) { Exit }
# Reboot the endpoint
Try { Restart-Computer -Force -ErrorAction Stop }
2023-10-05 13:11:38 -04:00
Catch { Write-Error $_ . Exception . Message }
2023-09-07 10:59:55 -04:00
}
2021-08-05 09:42:15 -04:00
Setup-MSPDirectories
2023-09-15 16:46:10 -04:00
Setup-LogFile
2023-09-18 16:19:21 -04:00
#SetToolsScriptLocation