Compare commits

...

16 Commits

Author SHA1 Message Date
45c403e19b Made new function an actual function 2025-06-04 16:54:51 -05:00
cd2aebf1ff More changes to make platform-agnostic 2025-06-04 16:43:25 -05:00
ddef660727 Finish out Import-Config... 2025-05-14 17:44:44 -05:00
d02ab2b22b Add Import-Config function 2025-05-14 17:43:12 -05:00
10458daf98 Fix typo 2025-05-14 17:42:54 -05:00
3a427ec324 Update tools and template 2025-05-14 16:40:31 -05:00
262c8fb095 Remove uninstall flag 2025-03-01 17:56:13 -06:00
aa5b347f4f Additional rework of template 2025-03-01 17:29:35 -06:00
e7c447d265 Troubleshooting script elevator 2025-03-01 17:07:30 -06:00
99a82e5e95 Minor fix to template 2025-02-28 23:38:24 -06:00
f5bfd672ff Revert function text output 2025-02-28 17:27:31 -06:00
bcea6127db Fixed typo 2025-02-28 15:56:47 -06:00
eb3ff67eed Add missing function 2025-02-28 15:54:07 -06:00
950f9b63f8 Update tools and template 2025-02-28 15:51:21 -06:00
ce8db4db9a Created a template file for script bases 2025-01-28 12:14:23 -06:00
c014bc7ec5 Add additional function, require PS 6.2 requirement 2025-01-28 12:14:03 -06:00
2 changed files with 606 additions and 59 deletions

View File

@@ -0,0 +1,157 @@
<#
.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
.\Script-Name.ps1 -Output "Example of how to run the script"
.LINK
https://example.com/docs
.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,
# .PARAMETER ScriptDir
# Allows for the script logs to output to a nonstandard location. Overrides user/computer script paths
[Parameter(Mandatory = $false)] [String] $ScriptDir,
# .PARAMETER NoLog
# Flag to disable log file output
[Parameter(Mandatory = $false)] [Switch] $NoLog
#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 "$([System.IO.Path]::GetTempPath())\$(Split-Path -Path $Uri -Leaf)" -ErrorAction Stop
Write-Host "Importing addon: $(Split-Path -Path $Uri -Leaf)"
Import-Module -Name "$([System.IO.Path]::GetTempPath())\$(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
}
}
Import-Modules $ModuleNames
#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:ProgramData\Mindfang\$ThisScript" }
"User" { $AppDir = "$Env:AppData\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 "$LogDir\$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

View File

@@ -1,9 +1,7 @@
#Requires -Version 6.2
Function Test-CommandExists {
<#
.SYNOPSIS
A brief description of the function or script.
Returns true if a command is present in PowerShell
.DESCRIPTION
A longer description.
@@ -34,23 +32,13 @@ Function Test-CommandExists {
Detail on what the script does, if this is needed.
#>
Param (
$Command
[Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $Command
)
$OldPreference = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
Try {
If (Get-Command $Command) {
Return $true
}
}
Catch {
Return $false
}
Finally {
$ErrorActionPreference = $OldPreference
If (Get-Command $Command -ErrorAction Stop) { Return $true }
}
Catch { Return $false }
}
Function Add-LogEntry {
@@ -87,35 +75,24 @@ Function Add-LogEntry {
Detail on what the script does, if this is needed.
#>
Param (
[Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()]$Message,
[Parameter(Mandatory = $false)] [ValidateSet('Notify', 'Warning', 'Error')]$As,
[Parameter(Mandatory = $false)] [Switch]$NewLine
[Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $Message,
[Parameter(Mandatory = $false)] [ValidateSet('Notify', 'Warning', 'Error', 'Debug')] [String] $As,
[Parameter(Mandatory = $false)] [Switch] $NewLine
)
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Switch ($As) {
"Notify" {
$FGC = "Cyan"
}
"Warning" {
$FGC = "Yellow"
}
"Error" {
$FGC = "Red"
}
Default {
$FGC = "White"
}
"Notify" { $FGC = "Cyan" }
"Warning" { $FGC = "Yellow" }
"Error" { $FGC = "Red" }
"Debug" { $FGC = "Magenta" }
Default { $FGC = "White" }
}
If ($Newline) {
Write-Host "`n[$Timestamp] $Message" -ForegroundColor $FGC
}
Else {
Write-Host "[$Timestamp] $Message" -ForegroundColor $FGC
}
If ($Newline) { $Output += "`n" }
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$Output += "[$Timestamp] $Message"
If ($As -eq "Debug") { If ($Dev) { Write-Host $Output -ForegroundColor $FGC } }
Else { Write-Host $Output -ForegroundColor $FGC }
}
Function Add-ListItem () {
@@ -152,14 +129,17 @@ Function Add-ListItem () {
Detail on what the script does, if this is needed.
#>
Param (
[Parameter(Mandatory = $true)] [ValidateNotNullorEmpty()]$Message
[Parameter(Mandatory = $true)] [ValidateNotNullorEmpty()] [String] $Message,
[Parameter(Mandatory = $false)] [Int16] $Indent = 0
)
Write-Host " * $Message"
For ($Counter = 0; $Counter -le $Indent; $Counter++) { $Output += " " }
$Output += "* $Message"
Write-Host $Output
}
Function Start-Logging {
Function Initialize-PSGallery {
<#
.SYNOPSIS
A brief description of the function or script.
@@ -193,28 +173,438 @@ Function Start-Logging {
Detail on what the script does, if this is needed.
#>
Param (
[Parameter(Mandatory)][String]$ScriptName,
[String]$LogDir,
[Switch]$User,
[Switch]$Append
)
If (Not $LogDir -eq $null) {
If ($User) {
$LogDir = "$Env:USERPROFILE\Mindfang\$ScriptName"
Add-LogEntry "Preparing system to download modules"
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)
}
Else {
$LogDir = "$Env:ProgramData\Mindfang\$ScriptName"
Catch {
Add-LogEntry "Upgrade failed, script is exiting" -As Error
Exit 1
}
}
If ($Append) {
Add-LogEntry (Start-Transcript -Path "$LogDir\$ScriptName.log" -UseMinimalHeader -Append)
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
}
Catch {
Add-LogEntry "Installation failed, script is exiting" -As Error
Exit 1
}
}
$PSRepos = Get-PSRepository
If ($PSRepos.Name -notcontains "PSGallery") {
Add-LogEntry "Registering PSGallery as script source"
Try {
Register-PSRepository -Default -InstallationPolicy Trusted -ErrorAction Stop
}
Catch {
Add-LogEntry "Change failed, script is exiting" -As Error
Exit 1
}
}
ElseIf ($PSRepos | Where-Object { $_.Name -eq "PSGallery" -and $_.InstallationPolicy -ne "Trusted" }) {
Add-LogEntry "Trusting scripts from PSGallery"
Try {
Set-PSRepository PSGallery -InstallationPolicy Trusted -ErrorAction Stop
}
Catch {
Add-LogEntry "Change failed, script is exiting" -As Error
Exit 1
}
}
}
Function Import-Modules {
<#
.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
Detail on what the script does, if this is needed.
#>
Param (
[Parameter(Mandatory=$true)] $ModuleList
)
If ($ModuleList) {
Initialize-PSGallery
ForEach ($Module in $ModuleList) {
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 1
}
}
Import-Module $Module
}
}
}
Function Import-JSONConfig () {
<#
.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
Detail on what the script does, if this is needed.
#>
Param (
[Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $ConfigFile
)
If (Test-Path -Path $ConfigFile) {
Try {
Return (Get-Content -Raw -Path $ConfigFile | ConvertFrom-Json)
}
Catch {
Add-LogEntry "Error loading config file! Please make sure the correct path was provided, and that it is a properly formatted JSON file" -As Error
Write-Output $_
Exit 1
}
}
Else {
Add-LogEntry (Start-Transcript -Path "$LogDir\$ScriptName.log" -UseMinimalHeader)
Add-LogEntry "Error loading config file! Please make sure the correct path was provided, and that it is a properly formatted JSON file" -As Error
Write-Output $_
Exit 1
}
}
Function Import-YAMLConfig () {
<#
.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
Detail on what the script does, if this is needed.
#>
Param (
[Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $ConfigFile
)
If ( -Not (Get-Module -ListAvailable -Name "powershell-yaml")) {
Add-LogEntry "Module `"powershell-yaml`" not present, but needed for YAML imports. Adding..."
Try {
Initialize-PSGallery
Install-Module -Name "powershell-yaml" -Scope CurrentUser -Force -AllowClober
}
Catch {
Add-LogEntry "Module failed to install automatically! Manaully install the module, then re-run the script." -As Error
Exit 1
}
}
If (Test-Path -Path $ConfigFile) {
Try {
Return (Get-Content -Raw -Path $ConfigFile | ConvertFrom-Yaml)
}
Catch {
Add-LogEntry "Error loading config file! Please make sure the correct path was provided, and that it is a properly formatted JSON file" -As Error
Write-Output $_
Exit 1
}
}
Else {
Add-LogEntry "Error loading config file! Please make sure the correct path was provided, and that it is a properly formatted JSON file" -As Error
Write-Output $_
Exit 1
}
}
Function Import-CSVConfig () {
<#
.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
Detail on what the script does, if this is needed.
#>
Param (
[Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] $ConfigFile
)
If (Test-Path -Path $ConfigFile) {
Try {
Return (Import-Csv $ConfigFile)
}
Catch {
Add-LogEntry "Error loading config file! Please make sure the correct path was provided, and that it is a properly formatted CSV file" -As Error
Write-Host $_
Exit
}
}
Else {
Add-LogEntry "Error loading config file! Please make sure the correct path was provided, and that it is a properly formatted CSV file" -As Error
Write-Host $_
Exit
}
}
Function Import-Config () {
<#
.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
Detail on what the script does, if this is needed.
#>
Param (
[Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] $ConfigFile
)
If (Test-Path -Path $ConfigFile) {
Try {
Switch ((Get-Item -Path $ConfigFile).Extension.ToLower()) {
{ @( ".yml", ".yaml").Contains($_) } { Return (Import-YAMLConfig $ConfigFile) }
{ @( ".jsn", ".json").Contains($_) } { Return (Import-JSONConfig $ConfigFile) }
{ @( ".csv").Contains($_) } { Return (Import-CSVConfig $ConfigFile) }
}
}
Catch {
Add-LogEntry "Error loading config file! Please make sure the correct path was provided, and that it is a properly formatted CSV file" -As Error
Write-Host $_
Exit
}
}
Else {
Add-LogEntry "Error loading config file! Please make sure the correct path was provided, and that it is a properly formatted file" -As Error
Write-Host $_
Exit
}
}
Function Set-RegistryKey {
Param(
[Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] $TestPath,
[Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] $TestKey,
[Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] $TestValue,
[Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] $TestType
)
If (-Not (Test-Path -Path $TestPath)) {
Add-LogEntry "Registry path not found, creating..."
New-Item $TestPath -Force -ErrorAction SilentlyContinue | Out-Null
}
If ($null -ne (Get-Item -Path $TestPath).GetValue($TestKey)) {
If ((Get-ItemPropertyValue -Path $TestPath -Name $TestKey) -eq $TestValue) {
Add-LogEntry "Current key value is `"$((Get-Item -Path $TestPath).GetValue($TestKey))`", no action needed"
}
Else {
Add-LogEntry "Setting value of `"$($TestKey)`" to `"$($TestValue)`""
Set-ItemProperty -Path $TestPath -Name $TestKey -Value $TestValue
}
}
Else {
Add-LogEntry "Registry key does not exist, creating..."
New-ItemProperty -Path $TestPath -Name $TestKey -Value $TestValue -PropertyType $TestType -Force -ErrorAction SilentlyContinue | Out-Null
}
}
Function Save-Parameters {
Param ([hashtable]$NamedParameters)
Return ($NamedParameters.GetEnumerator() | ForEach-Object { "-$($_.Key) `"$($_.Value)`"" }) -join " "
}
Function Test-AsAdmin () {
<#
.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
Detail on what the script does, if this is needed.
#>
Param (
[Parameter(Mandatory = $false)] [String] $AdditionalFlags
)
Add-LogEntry "Checking to see if script has been run with administrative privileges"
if (-Not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) {
Add-LogEntry "Performing script elevation, please approve the admin prompt"
Start-Sleep -Seconds 3
if ([int](Get-CimInstance -Class Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber) -ge 6000) {
$CommandLine = "-ExecutionPolicy Bypass -File `"" + $MyInvocation.MyCommand.Path + "`" " + (Save-Parameters $MyInvocation.BoundParameters) + " " + $MyInvocation.UnboundArguments
Start-Process -FilePath PowerShell.exe -Verb Runas -ArgumentList $CommandLine
Exit
}
}
Else {
Add-LogEntry "Script already running as admin"
}
}
# Origin: https://lucyllewy.com/powershell-clickable-hyperlinks/
Function Format-Hyperlink {
Param(
[Parameter(ValueFromPipeline = $true, Position = 0)][ValidateNotNullOrEmpty()] [Uri] $Uri,
[Parameter(Mandatory = $false, Position = 1)] [String] $Label
)
If (($PSVersionTable.PSVersion.Major -lt 6 -or $IsWindows) -and -not $Env:WT_SESSION) {
# Fallback for Windows users not inside Windows Terminal
If ($Label) {
Return "$Label ($Uri)"
}
Return "$Uri"
}
If ($Label) {
Return "`e]8;;$Uri`e\$Label`e]8;;`e\"
}
Return "$Uri"
}