<# .SYNOPSIS A brief description of the function or script. .DESCRIPTION A longer description. .PARAMETER FirstParameter Description of each of the parameters. Note: To make it easier to keep the comments synchronized with changes to the parameters, the preferred location for parameter documentation comments is not here, but within the param block, directly above each parameter. .PARAMETER SecondParameter Description of each of the parameters. .INPUTS Description of objects that can be piped to the script. .OUTPUTS Description of objects that are output by the script. .EXAMPLE Example of how to run the script. .LINK Links to further documentation. .NOTES NAME: AUTHOR: LASTEDIT: VERSION: #> #region Parameters param ( # Allows modules required by the script to be predefined, and loaded or installed as needed [Parameter(DontShow = $true)] [Array] $ModuleNames = @(), # Allows import of addon scripts [Parameter(Dontshow = $true)] [Array] $AddonUris = @( "https://gitea.taco.quest/Mindfang/ProjectTools/raw/branch/main/PSUtilities/ScriptTools.ps1" ), # Sets the script storage location [Parameter(DontShow = $true)] [ValidateSet('System', 'User', 'Custom')] [String] $ScriptType = "System", # Allows adding verbose logging and what-if command simulation to the script [Parameter(DontShow = $true)] [Switch] $Dev, # Allows for the script logs to output to a nonstandard location. Overrides user/computer script paths [Parameter(Mandatory = $false)] [String] $ScriptDir, # Flag to disable log file output [Parameter(Mandatory = $false)] [Switch] $NoLog, # Allows for uninstallation of script [Parameter(Mandatory = $false)] [Switch] $Uninstall #TODO: Add additional parameters, if appropriate ) #endregion Parameters #region Architecture Check # Verifies script is running in 64-bit PowerShell, if available. Allows for parameters to be passed to relaunched script # Origin: https://z-nerd.com/blog/2020/03/31-intune-win32-apps-powershell-script-installer/ If ($Env:PROCESSOR_ARCHITECTURE -ne "AMD64") { If (Test-Path -Path "$Env:WinDir\SysNative\WindowsPowerShell\v1.0\powershell.exe" -PathType Leaf) { $ArgsString = "" Try { ForEach ($Key in $MyInvocation.BoundParameters.Keys) { Switch ($MyInvocation.BoundParameters[$Key].GetType().Name) { "SwitchParameter" { if ($MyInvocation.BoundParameters[$Key].IsPresent) { $ArgsString += "-$Key " } } "String" { $ArgsString += "-$Key `"$($MyInvocation.BoundParameters[$Key])`" " } "Int32" { $ArgsString += "-$Key $($MyInvocation.BoundParameters[$Key]) " } "Boolean" { $ArgsString += "-$Key `$$($MyInvocation.BoundParameters[$Key]) " } } } Start-Process -FilePath "$ENV:WINDIR\SysNative\WindowsPowershell\v1.0\PowerShell.exe" -ArgumentList ` "-ExecutionPolicy Bypass -NoProfile -File $PSCommandPath $ArgsString" -Wait -NoNewWindow } Catch { Throw "Failed to start `"$PSCommandPath`" in 64-bit PowerShell" } Exit $LASTEXITCODE } } #endregion Architecture Check #region Imports # Import script addons, provided by URI ForEach ($Uri in $AddonUris) { $UriStatusCode = (Invoke-WebRequest -Uri $Uri -UseBasicParsing -Method Head -SkipHttpErrorCheck).StatusCode If ($UriStatusCode -eq 200) { Try { Invoke-WebRequest -Uri $Uri -OutFile "$Env:Temp\$(Split-Path -Path $Uri -Leaf)" -ErrorAction Stop Write-Host "Importing addon: $(Split-Path -Path $Uri -Leaf)" Import-Module -Name "$Env:Temp\$(Split-Path -Path $Uri -Leaf)" -Force } Catch { Write-Host "Error encountered importing addon" Exit $LASTEXITCODE } } Else { Write-Host "Unable to import addon `"$(Split-Path -Path $Uri -Leaf)`" - verify URI is correct and that this device has an internet connection" Exit $UriStatusCode } } # Load any modules required by script If ($ModuleNames) { If ([Net.ServicePointManager]::SecurityProtocol -ne [Net.SecurityProtocolType]::SystemDefault) { Add-LogEntry "Upgrading TLS security protocol to 1.2" Try { [Net.ServicePointManager]::SecurityProtocol = @([Net.SecurityProtocolType]::Tls, [Net.SecurityProtocolType]::Tls11, [Net.SecurityProtocolType]::Tls12) } Catch { Add-LogEntry "TLS upgrade failed, script is exiting" -As Error Exit } } If ((Get-PackageProvider).Name -notcontains "NuGet") { Add-LogEntry "Installing NuGet package provider" Try { Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -ErrorAction Stop | Out-Null } Catch { Add-LogEntry "Installation failed, script is exiting" -As Error Exit } } If ((Get-PSRepository).Name -notcontains "PSGallery") { Add-LogEntry "Registering PSGallery as script source" Try { Register-PSRepository -Default -InstallationPolicy Trusted -ErrorAction Stop } Catch { Add-LogEntry "Unable to register PSGallery, script is exiting" -As Error Exit } } ElseIf (Get-PSRepository | Where-Object { $_.Name -eq "PSGallery" -and $_.InstallationPolicy -ne "Trusted" }) { Add-LogEntry "Trusting packages from PSGallery" Try { Set-PSRepository PSGallery -InstallationPolicy Trusted -ErrorAction Stop } Catch { Add-LogEntry "Unable to set PSGallery as trusted, script is exiting" -As Error Exit } } ForEach ($Module in $ModuleNames) { If (-Not (Get-Module -ListAvailable -Name $Module)) { Try { Add-LogEntry "Module `"$Module`" not found, installing" Install-Module -Name $Module -Scope CurrentUser -Force -AllowClobber } Catch { Add-LogEntry "Module failed to install automatically! Manaully install the module, then re-run the script." -As Error Exit } } Import-Module $Module } } #endregion Imports #region Functions #endregion Functions #region Prep # Set the working directory for logs and additional files $ThisScript = ([IO.FileInfo]$MyInvocation.MyCommand.Definition).BaseName Switch ($ScriptType) { "System" { $AppDir = "$Env:AppData\Mindfang\$ThisScript" } "User" { $AppDir = "$Env:ProgramData\Mindfang\$ThisScript" } "Custom" { If ($ScriptDir) { $AppDir = $ScriptDir } Else { $AppDir = ([IO.FileInfo]$MyInvocation.MyCommand.Definition).Directory } } } # Create specified appdir if it does not already exist If (-Not (Test-Path $AppDir)) { New-Item $AppDir -ItemType Directory } # Begin recording transcript file If (-Not $NoLog) { If (-not $LogDir) { $LogDir = $AppDir } Add-LogEntry (Start-Transcript "$AppDir\$ThisScript.log" -Append) } #endregion Prep #region Execution Try { #TODO: Add script actions here } Catch { # Write error to logs, if an exception is caught Write-Host "Script execution failed!" Write-Host $_ Write-Host $_.InvocationInfo Write-Host $_.ScriptStackTrace Exit $LASTEXITCODE } Finally { # Stop transcript, even if an error has occurred If (-Not $NoLog) { Stop-Transcript } #TODO: Perform any additional disconnects, if needed } #endregion Execution