<# .SYNOPSIS Script Template .DESCRIPTION - Intended to be a boilerplate / template for writing powershell scripts .PARAMETER Testing Testing argument, will show verbose output and pause at certain script locations .PARAMETER PathLogs Root directory where logs are generated/cleaned up .PARAMETER ScriptFilePrefix File prefix for generated files like logs, used for consistency and cleanup .EXAMPLE PS C:\> # Run script locally PS C:\> .\_Script-Template.ps1 .EXAMPLE PS C:\> # Run script remotely PS C:\> Set-ExecutionPolicy -ExecutionPolicy Bypass PS C:\> iex "& { $(irm https://wobig.tech/downloads/scripts/_Script-Template.ps1) }" .NOTES Author: Rick Wobig Source: https://wobig.tech/downloads/scripts/_Script-Template.ps1 #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [Switch] [bool]$Testing = $false, [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$PathLogs = "$([System.Environment]::GetEnvironmentVariable('TEMP','Machine'))\", [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$ScriptFilePrefix = "Script_" ) #region Script Execution try { # Instantiate Logger and indicate the script has started [Logger]::new($Testing, $PathLogs, $ScriptFilePrefix) | Out-Null [Logger]::Logger.LogDisplay("Starting script execution", "Cyan") # Implement Core Logic # Finish up [Logger]::Logger.LogDisplay("Stopping script execution", "Cyan") } catch { [HouseKeeping]::StopOnFailure("GLOBAL FAILURE: $($PSItem.ToString())") } #endregion # region Classes class Logger { static [Logger] $Logger [bool] hidden $TestMode [string] hidden $LogPath [string] hidden $FilePrefix [string] hidden $LogDate = "$(Get-Date -Format %M_%d)" Logger( [bool]$testing, [string]$pathLogs, [string]$filePrefix ) { $this.TestMode = $testing $this.LogPath = $pathLogs $this.FilePrefix = $filePrefix [Logger]::Logger = $this [Logger]::GenerateLogDir($this.LogPath) } Log($message) { $methodName = [Logger]::GetMethodName(2) Add-Content "$($this.LogPath)$($this.FilePrefix)$($this.LogDate).log" "[$(Get-Date -UFormat %H:%M:%S)] $($methodName): $($message)" if ($this.TestMode) { Write-Host -fore DarkGray "[$(Get-Date -UFormat %H:%M:%S)] $($methodName): $($message)" } } LogPause($message) { if ($this.TestMode) { $methodName = [Logger]::GetMethodName(2) Add-Content "$($this.LogPath)$($this.FilePrefix)$($this.LogDate).log" "[$(Get-Date -UFormat %H:%M:%S)] $($methodName): $($message)" Write-Host -fore DarkGray "[$(Get-Date -UFormat %H:%M:%S)] $($methodName): $($message)" Pause } } LogDisplay($message, $color = "Gray"){ $methodName = [Logger]::GetMethodName(2) Add-Content "$($this.LogPath)$($this.FilePrefix)$($this.LogDate).log" "[$(Get-Date -UFormat %H:%M:%S)] $($methodName): $($message)" if ($this.TestMode){ Write-Host -fore $color "[$(Get-Date -UFormat %H:%M:%S)] $($methodName): $($message)" return } Write-Host -fore $color "[$(Get-Date -UFormat %H:%M:%S)] $($message)" } LogLoading() { if (!($this.TestMode)) { Add-Content "$($this.LogPath)$($this.FilePrefix)$($this.LogDate).log" "." Write-Host -fore DarkGray -NoNewline "." } } static [string] GetMethodName([int]$StackNumber = 1) { return [string]$(Get-PSCallStack)[$StackNumber].FunctionName.ToUpper() } static [string] GenerateLogDir($logDir) { try { if (!(Test-Path -Path $logDir)) { New-Item $logDir -ItemType Directory } return $logDir } catch { Write-Host -fore Red "FAILURE: $($PSItem.ToString())" return $null } } } class HouseKeeping { static [void] FinishScriptExecution($startTime){ [Logger]::Logger.LogDisplay("Script Finished", "Cyan") $endTime = Get-Date [Logger]::Logger.LogDisplay("Total script time: $(($endTime - $startTime).TotalSeconds)", "Cyan") Exit } static [void] StopOnFailure($exitMessage) { if ($null -ne [Logger]::Logger) { [Logger]::Logger.LogDisplay("$exitMessage | Stopping script execution", "Red") } Exit } static [void] EnsureScriptElevation($currentDirectory){ # Check if the script is running with administrative privileges $isAdmin = [bool]([System.Security.Principal.WindowsIdentity]::GetCurrent()).IsAdmin [Logger]::Logger.Log("Script run as admin: $isAdmin") if (-not $isAdmin) { # Relaunch the script with administrative privileges [Logger]::Logger.Log("Script isn't running with elevated permissions, attempting to elevate") Start-Process powershell.exe -WorkingDirectory $currentDirectory -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs exit } [Logger]::Logger.LogDisplay("Script has admin permissions! Continuing script execution", "Green") } } class IO { static [bool] RemoveFolderIfExist($folderPath){ try { if (Test-Path -PathType Container $folderPath) { Remove-Item $folderPath -Recurse -Force -ErrorAction SilentlyContinue [Logger]::Logger.Log("Removed folder recursively: $folderPath") return $true } else { [Logger]::Logger.Log("Folder doesn't exist, skipping removal: $folderPath") return $true } } catch { [Logger]::Logger.LogDisplay("FAILURE: $($PSItem.ToString())", "Red") return $false } } static [bool] CreateFolderIfNotExist($folderPath){ # Create folder path if it doesn't exist if (!(Test-Path -PathType Container $folderPath)) { try { New-Item -Path $folderPath -ItemType "directory" | Out-Null [Logger]::Logger.Log("Created non-existant folder: $folderPath") return $true } catch { [Logger]::Logger.LogDisplay("FAILURE: $($PSItem.ToString())", "Red") return $false } } return $false } } # endregion