Last active
August 10, 2023 06:53
-
-
Save loopyd/ae5f5a50b679b98e0899e4748e8f5ce0 to your computer and use it in GitHub Desktop.
[Powershell] Blep.ps1 - ShadowPC setup in the Draconic Way.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
---------------------------------------------- | |
Blep - Draconic keystroke saver for ShadowPC | |
============================================== | |
Author: LoopyD <loopyd@github.com> | |
Version: 1.1.0 | |
License: Unlicense (https://unlicense.org) | |
---------------------------------------------- | |
INRODUCTION | |
============ | |
This script's intent is to configure and set up a ShadowPC environment for development work in an unattended way, and save time over Shadow resets by allowing a developer to have their coffee and watch the world burn around them instead of sitting in front of their Windows Terminal typing commands. | |
GET A SHADOW | |
============ | |
If you don't have a Shadow, you can use MY REFERRAL CODE for a 5 Euro discount: 814181C @ https://shadow.tech | |
RUN THE SCRIPT | |
============== | |
Download the script to any folder in your UserProile, and then open up a Powershell 2.0 terminal to it and run it. | |
CUSTOMIZE THE SCRIPT | |
==================== | |
You can also add a blep-config.json file to go next to the script to customize the script's behavior. See the CONFIGURATION section for more details. | |
BLEP API | |
======== | |
This script contains a full API that you can use to make non-destructive changes to your operating system in a data-driven way. You can use it to install packages, configure the registry, and modify the environment. You can also use the Blep API to create your own custom unattended scripts. The API is documented in the FUNCTONS section of this script. | |
SPECIAL THANKS | |
============== | |
- OpenAI for GPT-4, which I used to help me speed up some trickier refactoring tasks. | |
- Microsoft for Powershell 7, which I used to write this script. | |
- GitHub Copilot for Visual Studio Code, which I used to help me write code comments, and speed up some repetative coding tasks. | |
- The Powershell community for their help and support. | |
- The ShadowPC community for their help and support, and for just being #ShadowTeam ! | |
#> | |
<############################################## | |
# PRE-FLIGHT CHECKS | |
##############################################> | |
# Block non-admin execution of the script. We need admin to install chocolatey, winget, and scoop, to make registry changes, and to install some packages. | |
$IsAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") | |
if (-not $IsAdmin) { | |
Write-Error "You must run this script as an Administrator!" | |
exit 1 | |
} | |
# Check and ensure the script is running as Powershell 7, if it is not, install it silently and reloaunch the script as Powershell 7. We use a legacy Powershell version to install Powershell 7 if we need to, so we use some old backwards-compatible code to do this. | |
if ($PSVersionTable.PSVersion.Major -lt 7) { | |
if (-not (Test-Path pwsh)) { | |
Write-Host -ForegroundColor Green $("-" * 80) | |
Write-Warning -Message "Powershell 7 is not installed, and required by the script, attempting to install it now..." | |
$url = "https://github.com/PowerShell/PowerShell/releases/download/v7.3.6/PowerShell-7.3.6-win-x64.msi" | |
$output = "$env:TEMP\PPowerShell-7.3.6-win-x64.msi" | |
$client = New-Object System.Net.WebClient | |
$client.DownloadFile($url, $output) | |
$arguments = "/i `"$output`" /quiet /qn /norestart" | |
Start-Process "msiexec.exe" -ArgumentList $arguments -Wait | |
if ($LASTEXITCODE -ne 0) { | |
Write-Error -Message "Powershell 7 installation failed, exiting script" | |
Write-Host -ForegroundColor Green $("-" * 80) | |
exit 1 | |
} | |
} | |
Write-Warning -Message "This script requires Powershell 7 or higher to execute, attempting to relaunch script as Powershell 7..." | |
Write-Host -ForegroundColor Green $("-" * 80) | |
Start-Process pwsh -ArgumentList "-File $PSCommandPath" -Verb RunAs | |
exit $LASTEXITCODE | |
} | |
# Create a Powershell profile file if it doesn't exist. | |
if (-not $(Test-Path -Path "$PROFILE")) { | |
New-Item -ItemType File -Path "$PROFILE" | |
} | |
# Display the title. Its base64 encoded to provide a minor annoyance to changing it. You can unpack it with a base64 decoder | |
$title = "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgX19fX19fX19fX19fX18gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAsPT09OicuLCAgICAgICAgICAgIGAtLl8gICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBgOi5gLS0tLl9fICAgICAgICAgYC0uXyAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYDouICAgICBgLS0uICAgICAgICAgYC4gICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwuICAgICAgICBgLiAgICAgICAgIGAuICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICgsLCgsICAgIFwuICAgICAgICAgYC4gICBfX19fLC1gLiwgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgKCwnICAgICBgLyAgIFwuICAgLC0tLl9fX2AuJyAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICwgICwnICAsLS0uICBgLCAgIFwuOycgICAgICAgICBgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgIGB7RCwgeyAgICBcICA6ICAgIFw7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICBCTEVQID09PS0gICAgICBWLCwnICAgIC8gIC8gICAgLy8gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgIEFVVE9JTlNUQUxMRVIgICAgIGo7OyAgICAvICAsJyAsLS8vLiAgICAsLS0tLiAgICAgICwgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgXDsnICAgLyAgLCcgLyAgXyAgXCAgLyAgXyAgXCAgICwnLyAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBcICAgYCcgIC8gXCAgYCcgIC8gXCAgYC4nIC8gICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBgLl9fXywnICAgYC5fXywnICAgYC5fXywnICANCg0KPS09LT0tPS09LT0tPS09LT0tPS09LT0tPS09LT0tPS09LT0tPS09LT0tPS09LT0tPS09LT0tPS09LT0NCkkgTiBTIFQgQSBMIEwgQSBUIEkgTyBOICBJIE4gIFQgSCBFICBEIFIgQSBDIE8gTiBJIEMgIFcgQSBZDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLUxELQ0K" | |
Write-Host -ForegroundColor Red $([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($title))) | |
<############################################## | |
# CONFIGURATION | |
##############################################> | |
<# | |
Load the blep-config.json file if it exists for easy user | |
customization, otherwise use the built-in defaults. | |
The json file has the following format: | |
{ | |
"ChocolateyPackages": [ | |
"vcredist-all", | |
"nerd-fonts-SourceCodePro", | |
... | |
], | |
"WinGetPackages": [ | |
"JanDeDobbeleer.OhMyPosh", | |
"ffmpeg", | |
... | |
], | |
"ScoopPackages": [ | |
"neofetch" | |
], | |
"RegistryEntries": [ | |
{ | |
"path": "HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsUpdate", | |
"key": "TargetReleaseVersion", | |
"value": "1", | |
"keytype": "DWord" | |
}, | |
... | |
], | |
"PyEnvVersions": [ | |
{ | |
"version": "3.9.13", | |
"default": false | |
}, | |
... | |
] | |
} | |
You can customize your own blep-config.json file by copying the | |
example, and filling it out with what you'd like in a file named | |
"blep-config.json" that sits beside the script. | |
#> | |
$configPath = "$PSScriptRoot\blep-config.json" | |
if (Test-Path $configPath) { | |
$jsonContent = Get-Content $configPath | |
$config = $jsonContent | ConvertFrom-Json | |
$ChocolateyPackages = $config.ChocolateyPackages | |
$WinGetPackages = $config.WinGetPackages | |
$ScoopPackages = $config.ScoopPackages | |
$RegistryEntries = $config.RegistryEntries | |
$PyEnvVersions = $config.PyEnvVersions | |
} else { | |
Write-Warning -Message "blep-config.json file not found in the script directory, we will be using built in defaults." | |
# You can edit this string array to add or remove packages from the install list for chocolatey. | |
$ChocolateyPackages = @( | |
'vcredist-all', | |
'nerd-fonts-SourceCodePro', | |
'git', | |
'visualstudio-installer', | |
'visualstudio2019buildtools', | |
'visualstudio2019community', | |
'visualstudio2019-workload-azure', | |
'visualstudio2019-workload-nativedesktop', | |
'visualstudio2019-workload-nativecrossplat', | |
'visualstudio2019-workload-nativemobile', | |
'visualstudio2019-workload-nativegame', | |
'visualstudio2019-workload-manageddesktop', | |
'visualstudio2019-workload-managedgame', | |
'visualstudio2019-workload-vctools', | |
'visualstudio2019-workload-universal', | |
'visualstudio2019-workload-netweb', | |
'visualstudio2019-workload-data', | |
'visualstudio2019-workload-databuildtools', | |
'visualstudio2019-workload-universalbuildtools', | |
'visualstudio2019-workload-manageddesktopbuildtools', | |
'visualstudio2019-workload-xamarinbuildtools', | |
'visualstudio2019-workload-azurebuildtools', | |
'visualstudio2022buildtools', | |
'visualstudio2022community', | |
'visualstudio2022-workload-azure', | |
'visualstudio2022-workload-nativedesktop', | |
'visualstudio2022-workload-nativecrossplat', | |
'visualstudio2022-workload-nativemobile', | |
'visualstudio2022-workload-nativegame', | |
'visualstudio2022-workload-manageddesktop', | |
'visualstudio2022-workload-managedgame', | |
'visualstudio2022-workload-vctools', | |
'visualstudio2022-workload-universal', | |
'visualstudio2022-workload-netweb', | |
'visualstudio2022-workload-data', | |
'visualstudio2022-workload-databuildtools', | |
'visualstudio2022-workload-universalbuildtools', | |
'visualstudio2022-workload-manageddesktopbuildtools', | |
'visualstudio2022-workload-xamarinbuildtools', | |
'visualstudio2022-workload-azurebuildtools', | |
'vscode', | |
"7zip" | |
) | |
# You can edit this string array to add or remove packages from the install list for winget. | |
$WinGetPackages = @( | |
'JanDeDobbeleer.OhMyPosh', | |
'ffmpeg', | |
"Microsoft.WindowsTerminal", | |
"Microsoft.PowerToys", | |
"Microsoft.PowerShell" | |
) | |
# You can edit this string array to add or remove packages from the install list for scoop. | |
$ScoopPackages = @( | |
'neofetch' | |
) | |
# You can edit this array of hashtables to add or update registry entries | |
$RegistryEntries = @( | |
[hashtable]@{ | |
path = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' | |
key = 'TargetReleaseVersion' | |
value = '1' | |
keytype = 'DWord' | |
}, | |
[hashtable]@{ | |
path = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' | |
key = 'TargetReleaseVersionInfo' | |
value = '22H2' | |
keytype = 'String' | |
}, | |
[hashtable]@{ | |
path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize' | |
key = 'AppsUseLightTheme' | |
value = '0' | |
keytype = 'DWord' | |
}, | |
[hashtable]@{ | |
path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize' | |
key = 'ColorPrevalence' | |
value = '1' | |
keytype = 'DWord' | |
}, | |
[hashtable]@{ | |
path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize' | |
key = 'EnableTransparency' | |
value = '1' | |
keytype = 'DWord' | |
}, | |
[hashtable]@{ | |
path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\ImmersiveShell' | |
key = 'TabletMode' | |
value = '0' | |
keytype = 'DWord' | |
}, | |
[hashtable]@{ | |
path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\ImmersiveShell' | |
key = 'ConvertibleSlateModePromptPreference' | |
value = '0' | |
keytype = 'DWord' | |
}, | |
[hashtable]@{ | |
path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' | |
key = 'HideFastUserSwitching' | |
value = '1' | |
keytype = 'DWord' | |
} | |
) | |
# You can add specific python versions here to use with pyenv. | |
$PyEnvVersions = @( | |
[hashtable]@{ | |
version = '3.9.13' | |
default = $false | |
}, | |
[hashtable]@{ | |
version = '3.10.11' | |
default = $true | |
}, | |
[hashtable]@{ | |
version = '3.11.4' | |
default = $false | |
} | |
) | |
} | |
<############################################## | |
# FUNCTIONS | |
##############################################> | |
<# | |
.SYNOPSIS | |
This function allows you to add or update registry entries. | |
.DESCRIPTION | |
This function allows you to add or update registry entries. You can specify the registry entries as a hashtable. This function offers a data-driven approach to editig your registry non-destructively. | |
.PARAMETER RegistryEntries | |
An array of hashtables containing the registry entries to add or update. | |
.EXAMPLE | |
$RegistryEntries = @( | |
[hashtable]@{ | |
path = "HKLM:\PATH\to\registry\key1" | |
key = "keyname" | |
value = "1" | |
keytype = "DWord" | |
}, | |
[hashtable]@{ | |
path = "HKLM:\PATH\to\registry\key2" | |
key = "keyname" | |
value = "FF FF 01 17 00 FC FF FF" | |
keytype = "QWord" | |
}, | |
) | |
Add-RegistryEntries -RegistryEntries $RegistryEntries | |
.NOTES | |
This function does not offer the capability to delete registry entries, as it is designed around working non-destructively. | |
#> | |
function Add-RegistryEntries { | |
param ( | |
[hashtable[]]$RegistryEntries | |
) | |
$addedKeys = @() | |
$updatedKeys = @() | |
$failedKeys = @() | |
Write-Host -ForegroundColor Green $("-" * 80) | |
ForEach ($entry in $RegistryEntries) { | |
$path = $entry["path"] | |
$keyName = $entry["key"] | |
$value = $entry["value"] | |
$keyType = $entry["keytype"] | |
if (($keyType -eq "DWord") -and $value.StartsWith("0b")) { | |
$value = [Convert]::ToInt64($value.Substring(2), 2) | |
} | |
if ($keyType -eq "Binary" -or $keyType -eq "QWord") { | |
$value = $value.Split(' ') | ForEach-Object { [Convert]::ToByte($_, 16) } | |
} | |
try { | |
if (-Not (Test-Path $path)) { | |
Write-Host -ForegroundColor Yellow "Creating path: $path" | |
$newPath = New-Item -Path $path -Force -ErrorAction SilentlyContinue | |
if ($null -eq $newPath) { | |
Write-Error "Failed to create registry path: $path" | |
$failedKeys += $keyName | |
continue | |
} | |
} | |
$existingValue = Get-ItemProperty -Path $path -Name $keyName -ErrorAction SilentlyContinue | |
if ($null -eq $existingValue) { | |
Write-Host "Adding key: $keyName at path: $path with value: $value and type: $keyType" | |
New-ItemProperty -Path $path -Name $keyName -Value $value -PropertyType $keyType | |
$addedKeys += $keyName | |
} else { | |
Write-Host "Updating key: $keyName at path: $path with value: $value and type: $keyType" | |
Set-ItemProperty -Path $path -Name $keyName -Value $value | |
$updatedKeys += $keyName | |
} | |
} catch { | |
Write-Error "Failed to edit registry key: $keyName at path: $path" | |
$failedKeys += $keyName | |
} | |
} | |
Write-Host -ForegroundColor Green $("-" * 80) | |
if ($addedKeys.Count -gt 0) { | |
$addString = "Added $($addedKeys.Count) key(s):`n" | |
$addedKeys | ForEach-Object { $addString += $($_ + " ") } | |
Write-Host -ForegroundColor Green $addString | |
} | |
if ($updatedKeys.Count -gt 0) { | |
$updateString = "Updated $($updatedKeys.Count) key(s):`n" | |
$updatedKeys | ForEach-Object { $updateString += $($_ + " ") } | |
Write-Host -ForegroundColor Green $updateString | |
} | |
if ($failedKeys.Count -gt 0) { | |
$failString = "Failed to edit $($failedKeys.Count) key(s):`n" | |
$failedKeys | ForEach-Object { $failString += $($_ + " ") } | |
Write-Error $failString | |
} | |
Write-Host -ForegroundColor Green $("-" * 80) | |
} | |
<# | |
.SYNOPSIS | |
This function allows you to add lines uniquely to a file. | |
.DESCRIPTION | |
This function allows you to add lines uniquely to a file. It will not add a line if it already exists in the file. Status will be displayed in the terminal. | |
.PARAMETER FilePath | |
The path to the file to modify. | |
.PARAMETER LinesToAdd | |
A multiline string of lines to add to the file. | |
.EXAMPLE | |
$FilePath = "C:\PATH\to\file.txt" | |
$LinesToAdd = @" | |
Line 1 | |
Line 2 | |
Line 3 | |
"@ | |
Add-UniqueLines -FilePath $FilePath -LinesToAdd $LinesToAdd | |
.NOTES | |
This function does not offer the capability to delete lines from a file, as it is designed around working non-destructively. | |
#> | |
function Add-UniqueLines { | |
param ( | |
[string]$FilePath, | |
[string]$LinesToAdd | |
) | |
Write-Host -ForegroundColor Green $("-" * 80) | |
Write-Host -ForegroundColor Yellow "Updating $FilePath..." | |
if (-Not (Test-Path -Path $FilePath)) { | |
Write-Error "Error: File '$FilePath' does not exist." | |
return | |
} | |
$linesAdded = 0 | |
$LinesToAdd.Split([Environment]::NewLine) | ForEach-Object { | |
$line = $_.Trim() | |
if ($line -and ((Get-Content -Path $FilePath | Select-String -Pattern ([regex]::Escape($line)) -Quiet) -eq $false)) { | |
Add-Content -Path $FilePath -Value $line | |
$linesAdded++ | |
} | |
} | |
if ($linesAdded -gt 0) { | |
Write-Host -ForegroundColor Green "Added $linesAdded lines to $FilePath" | |
} else { | |
Write-Warning "No lines added to $FilePath, leaving as-is." | |
} | |
Write-Host -ForegroundColor Green $("-" * 80) | |
} | |
<# | |
.SYNOPSIS | |
This function allows you to install packages from various package managers. | |
.DESCRIPTION | |
This function allows you to install packages from various package managers. It will not install a package if it is already installed, instead, it will try to update it. Status will be displayed in the terminal, along with a results summary. This function can install packages from Chocolatey, Winget, and Scoop. | |
.PARAMETER Packages | |
A string array of packages to install. | |
.PARAMETER PackageType | |
The package manager to use. Valid values are: choco, winget, scoop | |
.EXAMPLE | |
$Packages = @( | |
"7zip", | |
"git", | |
"notepadplusplus", | |
"vscode" | |
) | |
Install-Packages -Packages $Packages -PackageType "choco" | |
.NOTES | |
This function does not offer the capability to uninstall packages, as it is designed around working non-destructively. | |
#> | |
function Install-Packages { | |
param ( | |
[string[]]$Packages, | |
[ValidateSet("choco", "winget", "scoop", IgnoreCase = $true)] | |
[string]$PackageType | |
) | |
$result = Install-PackageManager -PackageManager $PackageType | |
if ($result -eq $false) { | |
Write-Error -Message "An error occoured with the package manager check, the script will now exit." | |
exit 1 | |
} | |
$succeededPackages = @() | |
$failedPackages = @() | |
ForEach ($Package in $Packages) { | |
Write-Host -ForegroundColor Green $("-" * 80) | |
switch ($PackageType.ToLower()) { | |
"choco" { | |
if ($null -eq $(choco list --local-only | Where-Object { $_ -like "$PackageName*" } )) { | |
Write-Host -Foreg roundColor Yellow "Installing $Package..." | |
choco install $Package --confirm | |
} else { | |
Write-Host -ForegroundColor Yellow "Package $Package is already installed, attempting to upgrade it, instead." | |
choco upgrade $Package --confirm | |
} | |
} | |
"winget" { | |
if ($null -eq $(winget list | Where-Object { $_ -like "$PackageName*" } )) { | |
Write-Host -ForegroundColor Yellow "Installing $Package..." | |
winget install $Package --disable-interactivity | |
} else { | |
Write-Host -ForegroundColor Yellow "Package $Package is already installed, attempting to upgrade it, instead." | |
winget upgrade $Package --disable-interactivity | |
} | |
} | |
"scoop" { | |
if ($null -eq $(scoop list | Where-Object { $_ -like "$PackageName*" } )) { | |
Write-Host -ForegroundColor Yellow "Installing $Package..." | |
scoop install $Package | |
} else { | |
Write-Host -ForegroundColor Yellow "Package $Package is already installed, attempting to upgrade it, instead." | |
scoop update $Package | |
} | |
} | |
} | |
$exitCode = $LASTEXITCODE | |
if ($exitCode -ne 0) { | |
Write-Host -ForegroundColor Green $("-" * 80) | |
$exitCode = $LASTEXITCODE | |
if ($exitCode -ne 0) { | |
Write-Error "Operation failed for $PackageType package: $Package" | |
$failedPackages += $Package | |
continue | |
} | |
} | |
$succeededPackages += $Package | |
refreshenv | |
. $PROFILE | |
} | |
Write-Host -ForegroundColor Green $("-" * 80) | |
if ($failedPackages.Count -gt 0) { | |
$errorString = "Failed to install $($failedPackages.Count) package(s):`n" | |
$failedPackages | ForEach-Object { $errorString += $($_ + " ") } | |
Write-Error $errorString | |
} | |
if ($succeededPackages.Count -gt 0) { | |
$successString = "Succeeded installing $($succeededPackages.Count) package(s):`n" | |
$succeededPackages | ForEach-Object { $successString += $($_ + " ") } | |
Write-Host -ForegroundColor Green $successString | |
} | |
Write-Host -ForegroundColor Green $("-" * 80) | |
} | |
<# | |
.SYNOPSIS | |
This function is able to install various package managers by using PowerShell. | |
.DESCRIPTION | |
This function is able to install various package managers by using PowerShell. It will not install a package manager if it is already installed. Status will be displayed in the terminal, along with a results summary. This function can install Chocolatey, Winget, and Scoop. | |
.PARAMETER PackageManager | |
The package manager to install. Valid values are: choco, winget, scoop | |
.EXAMPLE | |
Install-PackageManager -PackageManager "choco" | |
.NOTES | |
This function does not offer the capability to uninstall package managers, as it is designed around working non-destructively. | |
#> | |
function Install-PackageManager { | |
param ( | |
[ValidateSet("choco", "winget", "scoop", IgnoreCase = $true)] | |
[string]$PackageManager | |
) | |
switch ($PackageManager) { | |
"scoop" { | |
if ($null -eq $(Get-Command -Name "scoop" -ErrorAction SilentlyContinue)) { | |
Write-Warning -Message "Scoop is not installed. Installing Scoop..." | |
Start-Process pwsh -Verb RunAs -ArgumentList "-NoProfile -ExecutionPolicy Bypass -Command `"& { iwr -useb get.scoop.sh | iex }`"" | |
if ($LASTEXITCODE -ne 0 -or $null -eq $(Get-Command -Name "scoop" -ErrorAction SilentlyContinue)) { | |
Write-Error -Message "Failed to install Scoop." | |
return $false | |
} | |
. $PROFILE | |
} else { | |
Write-Host -ForegroundColor Green "Scoop is installed, proceeding" | |
return $true | |
} | |
} | |
"choco" { | |
if ($null -eq $(Get-Command -Name "choco" -ErrorAction SilentlyContinue)) { | |
Write-Warning "Chocolatey is not installed. Installing Chocolatey..." | |
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) | |
if ($LASTEXITCODE -ne 0 -or -not $(Test-Path -Path "$env:ChocolateyInstall")) { | |
Write-Error -Message "Failed to install Chocolatey" | |
return $false | |
} | |
Import-Module $env:ChocolateyInstall\helpers\chocolateyProfile.psm1 | |
refreshenv | |
. $PROFILE | |
choco feature enable -n allowGlobalConfirmation | |
} else { | |
Write-Host -ForegroundColor Green "Chocolatey is installed, proceeding" | |
Import-Module $env:ChocolateyInstall\helpers\chocolateyProfile.psm1 | |
refreshenv | |
. $PROFILE | |
return $true | |
} | |
} | |
"winget" { | |
if ($null -eq $(Get-Command -Name "winget" -ErrorAction SilentlyContinue)) { | |
Write-Warning -Message "Winget is not installed. Installing Winget..." | |
Install-Module -Name Microsoft.WinGet.Client | |
if ($LASTEXITCODE -ne 0 -or $null -eq $(Get-Command -Name "winget" -ErrorAction SilentlyContinue)) { | |
Write-Error -Message "Failed to install Winget, the script will now exit." | |
return $false | |
} | |
} else { | |
Write-Host -ForegroundColor Green "Winget is installed, proceeding" | |
return $true | |
} | |
} | |
} | |
} | |
<# | |
.SYNOPSIS | |
This function allows you to install different python versions via pyenv-win. | |
.DESCRIPTION | |
This function allows you to install different python versions via pyenv-win. If pyenv-win is not installed, it will be installed automatically for you into your $USERPROFILE\.pyenv directory and added to your environment. | |
.PARAMETER PyEnvVersions | |
A hashtable specifying a list of python versions to isntall into pyenv, along with some properties. | |
.EXAMPLE | |
$PyEnvVersions = @( | |
[hashtable]@{ | |
Version = "3.9.6" | |
Default = $true | |
}, | |
[hashtable]@{ | |
Version = "3.8.10" | |
Default = $false | |
} | |
) | |
Install-PyVersions -PyEnvVersions $PyEnvVersions | |
.NOTES | |
This function does not offer the capability to uninstall pyenv-win, as it is designed around working non-destructively. | |
#> | |
function Install-PyVersions() { | |
param( | |
[Parameter(Mandatory = $true)][hashtable[]]$PyEnvVersions | |
) | |
if ($PyEnvVersions.Count -eq 0) { | |
Write-Error -Message "No python versions specified to install, skipping python." | |
return | |
} | |
if ($null -eq $env:PYENV_HOME) { | |
Write-Warning -Message "pyenv-win is not installed. Installing pyenv-win..." | |
Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/pyenv-win/pyenv-win/master/pyenv-win/install-pyenv-win.ps1" -OutFile "./install-pyenv-win.ps1"; &"./install-pyenv-win.ps1" | |
if ($LASTEXITCODE -ne 0) { | |
Write-Error -Message "Failed to install pyenv-win" | |
return | |
} | |
refreshenv | |
. $PROFILE | |
} else { | |
Write-Host -ForegroundColor Green "pyenv-win is installed, proceeding" | |
} | |
$succeededVersions = @() | |
$failedVersions = @() | |
ForEach ($PyEnvVersion in $PyEnvVersions) { | |
$version = $PyEnvVersion['version'] | |
$default = $PyEnvVersion['default'] | |
if ($null -eq $(pyenv versions | Where-Object { $_ -like "$PyEnvVersion*" } )) { | |
Write-Host -ForegroundColor Green $("-" * 80) | |
Write-Host -ForegroundColor Yellow "Installing $version..." | |
pyenv install $version | |
if ($LASTEXITCODE -ne 0) { | |
Write-Error -Message "Failed to install $version." | |
$failedVersions += $version | |
continue | |
} | |
if ($default) { | |
pyenv global $version | |
} | |
Write-Host -ForegroundColor Green "Successfully installed $version." | |
refreshenv | |
. $PROFILE | |
$succeededVersions += $version | |
} else { | |
Write-Warning -Message "Version $version is already installed." | |
$succeededVersions += $version | |
if ($default) { | |
Write-Host -ForegroundColor Green "Setting $version as the default python version." | |
pyenv global $version | |
refreshenv | |
. $PROFILE | |
} | |
continue | |
} | |
} | |
Write-Host -ForegroundColor Green $("-" * 80) | |
if ($succeededVersions.Count -gt 0) { | |
Write-Host -ForegroundColor Green "Successfully installed the following versions: $($succeededVersions -join " ")." | |
} | |
if ($failedVersions.Count -gt 0) { | |
Write-Error -Message "Failed to install the following python versions: $($failedVersions -join " ")." | |
} | |
Write-Host -ForegroundColor Green $("-" * 80) | |
} | |
<# | |
.SYNOPSIS | |
This function allows you to automate the install of the NVIdia driver for your graphics card in your Shadow. | |
.DESCRIPTION | |
This function allows you to automate the install of the NVIdia driver for your graphics card in your Shadow. It will search for the graphics card information using the Windows Management Instrumentation and your OS information located in the registry, and then use that information to download the latest driver from NVidia's website by calling the internal GeForce experience update check API. It will then extract the driver and install it. | |
This is a very advanced function but makes the process of updating your graphics driver super simple. GeForce Experience is no longer required to have on your system with blep! | |
.EXAMPLE | |
Update-NVidiaDriver | |
.NOTES | |
WARNING: By calling this function, you agree to the NVidia EULA located at https://www.nvidia.com/en-us/drivers/nv-ueula/ , This function is NOT SUPPORTED by NVidia. Use at your own risk. | |
#> | |
function Update-NVidiaDriver() { | |
Write-Host -ForegroundColor Green $("-" * 80) | |
Write-Host -ForegroundColor Blue "Reading system information..." | |
$videoCard = Get-CimInstance -ClassName Win32_VideoController | Select-Object -First 1 | |
if ($videoCard.PNPDeviceID -match "VEN_10DE") { | |
$nvidiaInfo = [hashtable]@{ | |
"vendor" = $videoCard.PNPDeviceID -replace '.*VEN_([^&]+)&.*', '$1' | |
"device" = $videoCard.PNPDeviceID -replace '.*DEV_([^&]+)&.*', '$1' | |
"subsys" = $videoCard.PNPDeviceID -replace '.*SUBSYS_([^&]+)&.*', '$1' | |
"rev" = $videoCard.PNPDeviceID -replace '.*REV_([^\\]+)\\.*', '$1' | |
'version' = ($videoCard.DriverVersion.Replace('.', '')[-5..-1] -join '').insert(3, '.') | |
'isWHQL' = $true | |
'isDCH' = $false | |
'uri' = [string]::Empty | |
} | |
Write-Host -ForegroundColor Red "`nGraphics Card Information:" | |
Write-Host -ForegroundColor Yellow " Vendor: $($nvidiaInfo.vendor)" | |
Write-Host -ForegroundColor Yellow " Device: $($nvidiaInfo.device)" | |
Write-Host -ForegroundColor Yellow " Subsystem: $($nvidiaInfo.subsys)" | |
Write-Host -ForegroundColor Yellow " Revision: $($nvidiaInfo.rev)" | |
Write-Host -ForegroundColor Yellow " Current Driver Version: $($nvidiaInfo.version)" | |
$osInfo = [hashtable]@{ | |
'os_version' = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name "ReleaseId").ReleaseId | |
'os_build' = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name "CurrentBuild").CurrentBuild | |
'x86_64' = $env:PROCESSOR_ARCHITECTURE -eq "AMD64" -or $env:PROCESSOR_ARCHITEW6432 -eq "x86_64" | |
'language_code' = (Get-Culture).LCID | |
} | |
Write-Host -ForegroundColor Red "Operating System Information:" | |
Write-Host -ForegroundColor Yellow " Os Version: $($osInfo.os_version)" | |
Write-Host -ForegroundColor Yellow " Os Build: $($osInfo.os_build)" | |
Write-Host -ForegroundColor Yellow " Is 64-bit? $($osInfo.x86_64)" | |
Write-Host -ForegroundColor Yellow " Language Code: $($osInfo.language_code)`n" | |
Write-Host -ForegroundColor Green $("-" * 80) | |
Write-Host -ForegroundColor Blue "Calling NVidia's internal GeForce Experience API to check for driver updates..." | |
# NOTE: Please keep in mind that this only checks for either Windows 10, or Windows 11 drivers. Remember that blep is an API for ShadowPC, in which only these two operating systems are supported. You'll need to integrate a full osC lookup table if you want to support other Windows versions. | |
$query_obj = @{ | |
"dIDa" = @($("{0}_{1}_{2}_{3}" -f ($nvidiaInfo.device, $nvidiaInfo.vendor, $nvidiaInfo.subsys.Substring(0, 4), $nvidiaInfo.vendor))) | |
"osC" = if ($osInfo.os_version -eq "2009") { "10.0" } else { "11.0" } | |
"osB" = $osInfo.os_build | |
"is6" = if ($osInfo.x86_64) { "1" } else { "0" } | |
"lg" = [string]$osInfo.language_code | |
"iLp" = "0" | |
"prvMd" = "0" | |
"gcV" = "3.18.0.94" | |
"gIsB" = "0" | |
"GFPV" = $nvidiaInfo.version | |
"dch" = if ($nvidiaInfo.isDCH -eq $true) { "1" } else { "0" } | |
"upCRD" = "0" | |
"isCRD" = "0" | |
"isInst" = "1" | |
} | |
$ENDPOINT = 'https://gfwsl.geforce.com/nvidia_web_services/controller.gfeclientcontent.NG.php/com.nvidia.services.GFEClientContent_NG.getDispDrvrByDevid' | |
$url = "$ENDPOINT/$($query_obj | ConvertTo-Json -Compress)" | |
$headers = @{ | |
'User-Agent' = 'NvBackend/36.0.0.0' | |
} | |
try { | |
<# Calling the GFEClientContent_NG.getDispDrvrByDevid endpoint returns a JSON object with a citeria object that allows us to conditionally see if there is a new driver available by checking criteria.IsDispDriverNewer.state for "true". If there is an update available, the DriverAttributes.DownloadURLAdmin property contains the URL to the direct download of the driver, and esponse.DriverAttributes.clientUX.DriverUpdateDesktopNotification contains a useful toast notification for us to pull from the REST API. This is the same URL that is used by the GeForce Experience client to download the driver. Because we have the API, the Admin version of the driver is accessable to us, meaning we can automate installation of the driver without the need for the GeForce Experience client. | |
You'd better be smashing that Star button, right now. | |
Not exactly a hack on my part, though. | |
Source: https://github.com/keylase/nvidia-patch/blob/master/tools/nv-driver-locator/gfe_get_driver.py | |
Another reverse engineerer left this code around, so I went ahead and updated it to work with the latest version of GFEClient API to emulate the presence of Geforce Experience, without installing bloatware, and pass compatible parameters to it so that it worked cleanly. :-) #> | |
$response = Invoke-RestMethod -Uri $url -Headers $headers -Method Get -TimeoutSec 10 -RetryIntervalSec 3 -MaximumRetryCount 3 | |
if ($response.criteria.IsDispDriverNewer.state -eq "true") { | |
$nvidiaInfo.url = $response.DriverAttributes.DownloadURLAdmin | |
Write-Host -ForegroundColor Green $("-" * 80) | |
Write-Host -ForegroundColor Green "$($response.DriverAttributes.clientUX.DriverUpdateDesktopNotification.titleText)" | |
$nvidiaTempFolder = "$folder\NVIDIA" | |
if (-not $(Test-Path -Path $nvidiaTempFolder)) { | |
New-Item -Path $nvidiaTempFolder -ItemType Directory | Out-Null | |
} | |
$dlFile = "$nvidiaTempFolder\nvidia-$($nvidiaInfo.version).exe" | |
Write-Host -ForegroundColor Yellow "Downloading installer..." | |
Start-BitsTransfer -Source $nvidiaInfo.url -Destination $dlFile | |
if ($?) { | |
Write-Host -ForegroundColor Green "Download complete." | |
} | |
else { | |
Write-Error -Message "Download failed!" | |
} | |
# NOTE: This should probably be its own function for the blep API, but I am a bit lazy right now...dergs are sometimes. When sequential execution comes out for blep, I will be adding an extractor as an Action. | |
$extractFolder = "$nvidiaTempFolder\nvidia-$($nvidiaInfo.version)" | |
$filesToExtract = "Display.Driver HDAudio NVI2 PhysX EULA.txt ListDevices.txt setup.cfg setup.exe" | |
# Find 7-zip. If it's not installed, install it. | |
if ((Test-path HKLM:\SOFTWARE\7-Zip\) -eq $true) { | |
$7zpath = Get-ItemProperty -path HKLM:\SOFTWARE\7-Zip\ -Name Path | |
$7zpath = $7zpath.Path | |
$7zpathexe = $7zpath + "7z.exe" | |
if ((Test-Path $7zpathexe) -eq $true) { | |
Write-Host -ForegroundColor Green "7zip installed at: $7zpathexe" | |
} else { | |
Write-Host -ForegroundColor Yellow "7zip not installed. Installing from chocolatey..." | |
Install-Packages -Packages @( "7zip" ) -PackageType "choco" | |
$7zpath = Get-ItemProperty -path HKLM:\SOFTWARE\7-Zip\ -Name Path | |
$7zpath = $7zpath.Path | |
$7zpathexe = $7zpath + "7z.exe" | |
} | |
} else { | |
Write-Host -ForegroundColor Yellow "7zip not installed. Installing from chocolatey..." | |
Install-Packages -Packages @( "7zip" ) -PackageType "choco" | |
$7zpath = Get-ItemProperty -path HKLM:\SOFTWARE\7-Zip\ -Name Path | |
$7zpath = $7zpath.Path | |
$7zpathexe = $7zpath + "7z.exe" | |
} | |
Write-Host -ForegroundColor Cyan "Extracting NVIDIA installer files..." | |
Start-Process -FilePath $7zpathexe -NoNewWindow -ArgumentList "x -bso0 -bsp1 -bse1 -aoa $dlFile $filesToExtract -o""$extractFolder""" -wait | |
if ($?) { | |
Write-Host -ForegroundColor Green "Extraction completed successfully." | |
(Get-Content "$extractFolder\setup.cfg") | Where-Object { $_ -notmatch 'name="\${{(EulaHtmlFile|FunctionalConsentFile|PrivacyPolicyFile)}}' } | Set-Content "$extractFolder\setup.cfg" -Encoding UTF8 -Force | |
Write-Host -ForegroundColor Cyan "Installing NVIDIA drivers..." | |
Write-Host -foregroundColor Magenta $("WARNING! " * 5) | |
Write-Host -ForegroundColor Magenta "`nThis will install the NVIDIA driver without any user interaction. This WILL cause your Shadow to CRASH FOR A WHILE, and you'll need to re-run blep after your shadow reboots.`nMultistage execution with a registry-driven instruction pointer is a future update.`nPlease re-run blep manually a second time to complete the rest of the installation steps, for now." | |
Write-Host -ForegroundColor Magenta $("`nWARNING! " * 5) | |
Write-Host -ForegroundColor -Red "`nThe operation will start in 10 seconds. !! HOLD ONTO YOUR BUTT !!" | |
Start-Sleep -Seconds 10 | |
$install_args = "-passive -noreboot -noeula -nofinish -s -clean" | |
Start-Process -FilePath "$extractFolder\setup.exe" -ArgumentList $install_args -wait | |
Write-Host -ForegroundColor Red "Cleaning up..." | |
Remove-Item $nvidiaTempFolder -Recurse -Force | |
Write-Host -ForegroundColor Red "NVIDIA Driver installed!`nRebooting your Shaodow now..." | |
Restart-Computer -Force | |
if ($?) { | |
Write-Host -ForegroundColor Green "NVIDIA Driver installed!`nYou may need to reboot to finish installation." | |
} else { | |
Write-Error -Message "Installation failed!" | |
} | |
} else { | |
Write-Error -Message "Extraction failed!" | |
} | |
Write-Host -ForegroundColor Red "Cleaning up..." | |
Remove-Item $nvidiaTempFolder -Recurse -Force | |
} else { | |
Write-Host -ForegroundColor Red "No new driver updates available." | |
} | |
} | |
catch [System.Net.WebException] { | |
if ($_.Exception.Response.StatusCode -eq 404) { | |
throw "Retunred a 404 error when attempting to locate new driver information. This could mean that your graphics card isn't currently supported, or that it is reporting an invalid device ID. Please let the developoer know if you see this error, with script output." | |
} elseif ($_.Exception.Response.StatusCode -eq 500) { | |
throw "Retunred a 500 error when attempting to locate new driver information. This means the Geforce Experience servers are down. Please try again later. If this error persists, please let the developer know with the script output." | |
} | |
elseif ($_.Exception.Response.StatusCode -eq 503) { | |
throw "Retunred a 503 error when attempting to locate new driver information. This means the Geforce Experience servers have changed their API and this endpoint is no longer accessable. If this error persists, please let the developer know with the script output." | |
} else { | |
throw $_ | |
} | |
} | |
catch { | |
throw $_ | |
} | |
} | |
else { | |
Write-Warning -Message "No NVidia graphics card found." | |
} | |
Write-Host -ForegroundColor Green $("-" * 80) | |
} | |
<########################################################## | |
# MAIN SCRIPT | |
##########################################################> | |
Install-Packages -Packages $ChocolateyPackages -PackageType "choco" | |
Install-Packages -Packages $WinGetPackages -PackageType "winget" | |
Install-Packages -Packages $ScoopPackages -PackageType "scoop" | |
Add-RegistryEntries -RegistryEntries $RegistryEntries | |
# Add oh-my-posh to ps profile | |
$lines = @" | |
oh-my-posh init pwsh --config "$env:POSH_THEMES_PATH/hunk.omp.json" | Invoke-Expression | |
"@ | |
Add-UniqueLines -FilePath $PROFILE -LinesToAdd $lines | |
# Install pyenv-win and python versions | |
Install-PyVersions -PyEnvVersions $PyEnvVersions | |
#> | |
# Update NVidia driver (NEW!) | |
Update-NVidiaDriver |
Version 1.1.0:
New features:
- NVIDIA GRAPHICS DRIVER AUTO-UPDATE Automatically upgrades NVidia drivers to the latest version, and will reboot the Shadow automatically. This new feature is very advanced, and checks the currently installed graphics card using the WMI and the registry, along with a special NVIDIA API call to select the right driver.
Bugfixes:
- Minor typo that doesn't specify "blep-config.json" correctly. Fixed.
- Issue with scoop where a -y flag was being passed incorrectly during an upgrade.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Version 1.0.1:
New features:
blep-config.json
for full automation with included defaults.Bugfixes: