Skip to content

Instantly share code, notes, and snippets.

@QNimbus
Last active May 31, 2024 20:19
Show Gist options
  • Save QNimbus/83a86ee072d2b4dbfd13f2fe901796df to your computer and use it in GitHub Desktop.
Save QNimbus/83a86ee072d2b4dbfd13f2fe901796df to your computer and use it in GitHub Desktop.
Provisioning #provisioning

Windows 11 Deployment Script

Overview

This PowerShell script is designed to automate the setup and configuration of my Windows 11 environment. It streamlines the deployment process, ensuring a consistent and efficient setup.

Script: win11.ps1

Features

  • WinGet Installation: Installs Microsoft's package manager, WinGet, along with its dependencies, if not already installed.
  • Bloatware Removal: Systematically removes a predefined list of bloatware applications to streamline the Windows 11 experience.
  • Application Installation: Automates the installation of essential applications like Git, Visual Studio Code, Windows Terminal, and others via WinGet.
  • Windows Features and Services Configuration: Enables crucial Windows features and configures various services for optimal performance and usability.
  • Windows Subsystem for Linux (WSL2) Setup: Facilitates the setup of WSL2, including the installation of a preferred Linux distribution (Ubuntu-22.04).
  • Windows Registry Tweaks: Applies a series of registry tweaks for system optimization and customization, such as disabling telemetry, web search in Start Menu, Edge HubSidebar, Windows 11 widgets, and automatic Maps updates. Additionally, sets the display format for date and time, and modifies taskbar search settings.
  • Taskbar and Desktop Customization: Generates a taskbar layout and restores desktop icons, tailoring the user interface to enhance accessibility and ease of use.
  • System Cleanup: Performs a system-wide cleanup to remove residual files in specific directories.
  • Administrative Check: Ensures the script is executed with administrative privileges for proper functioning.

Dependencies

  • PowerShell 5.1 or higher
  • Internet connection for downloading necessary components

Usage

  1. Execute bootstrap script:

    PowerShell.exe -ExecutionPolicy Bypass { Invoke-RestMethod -Uri https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---bootstrap.ps1 | Invoke-Expression }

[!INFO] You can create a desktop shortcut with the following target C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -Command "Invoke-RestMethod -Uri https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---bootstrap_sandbox.ps1 | Invoke-Expression"

Important

Run this command in an elevated PowerShell terminal.

  1. Script Execution: The sandbox will automatically download and execute the deployment script upon launch, setting up the environment as per the script’s configuration.

Contributing

Contributions to this script are welcome. Please submit pull requests with your proposed changes.

License

This script is released under the MIT License.

Author

B. van Wetten

Last Updated: 2024-01-06

Windows 11 Sandbox Deployment Script

Overview

This PowerShell script is designed to automate the setup and configuration of a Windows 11 Sandbox environment. It streamlines the deployment process, ensuring a consistent and efficient setup.

Script: sandbox.ps1

Features

  • Scoop Installation: Automatically installs Scoop, a command-line installer for Windows, and configures its settings.
  • Windows Registry Configuration: Executes various Windows registry hacks to customize the Windows environment.
  • Desktop and Taskbar Customization: Sets up the desktop and taskbar layout, including creating shortcuts and modifying the taskbar layout.
  • Downloading and Setting Wallpaper: Downloads and sets a random wallpaper from Unsplash if no wallpaper is found in the designated folder.
  • Script Execution: Allows for the execution of additional install scripts (currently disabled by default).

Dependencies

  • Windows 11 Sandbox enabled (Enable-WindowsOptionalFeature -FeatureName "Containers-DisposableClientVM" -All -Online)
  • PowerShell 5.1 or higher
  • Internet connection for downloading necessary components

Usage

  1. Create a Windows Sandbox Config File: Create a .wsb file (e.g., %USERPROFILE%/Windows Sandbox config.wsb) with the necessary configuration to set up the sandbox environment. This includes enabling vGPU, setting networking options, and specifying other features like audio and video input, printer redirection, clipboard redirection, and protected client mode.

    Example configuration - See: Windows Sandbox.wsb

    <Configuration>
      <vGPU>Enable</vGPU>
      <Networking>Default</Networking>
      <AudioInput>Disable</AudioInput>
      <VideoInput>Disable</VideoInput>
      <PrinterRedirection>Disable</PrinterRedirection>
      <ClipboardRedirection>Default</ClipboardRedirection>
      <ProtectedClient>Enable</ProtectedClient>
      <MappedFolders>
          <MappedFolder>
              <HostFolder>C:\Users\bas\Downloads\.sandbox</HostFolder>
              <SandboxFolder>C:\Users\WDAGUtilityAccount\Downloads</SandboxFolder>
              <ReadOnly>False</ReadOnly>
          </MappedFolder>
      </MappedFolders>
        <LogonCommand>
            <Command>powershell -executionpolicy Unrestricted -Command "start powershell { -Command \"Set-ExecutionPolicy Unrestricted -Scope LocalMachine -Force; Invoke-WebRequest -UseBasicParsing -Uri 'https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---bootstrap_sandbox.ps1' | Select-Object -ExpandProperty Content | Invoke-Expression \"}"</Command>
        </LogonCommand>
    </Configuration>
  2. Run the Config File: Double-click on the .wsb file to start Windows Sandbox with the specified configuration.

  3. Script Execution: The sandbox will automatically download and execute the deployment script upon launch, setting up the environment as per the script’s configuration.

Important

Make sure the folder in <HostFolder /> exists and also ensure that the URLs and file paths in the .wsb file are accurate and relevant to your setup.

Contributing

Contributions to this script are welcome. Please submit pull requests with your proposed changes.

License

This script is released under the MIT License.

Author

B. van Wetten

Last Updated: 2024-01-06

# Title: Bootstrap script of Windows 11 deployment script
# Author: B. van Wetten
# Last Update: 2024-01-06
#
# Prerequisites: See: https://gist.github.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df#file-provisioning-md
#
# Download only: Invoke-WebRequest -Uri https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---bootstrap.ps1 -OutFile $env:userprofile\Downloads\bootstrap.ps1
# Usage: PowerShell.exe -ExecutionPolicy Bypass { Invoke-RestMethod -Uri https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---bootstrap.ps1 | Invoke-Expression }
# Start of script
Set-StrictMode -Version 1.0
Set-Variable -Name ProgressPreference -Value "SilentlyContinue" # Default is "Continue"
Set-Variable -Name ErrorActionPreference -Value "Stop" # Default is "Continue"
Write-Host "[Info] Starting bootstrap script" -ForegroundColor Yellow
# Download Powershell scripts from GitHub
[hashtable[]] $PowerShellScripts = @(
@{ destination = "$env:USERPROFILE\Downloads\scripts\win11.ps1"; url ='https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---win11.ps1'; }
@{ destination = "$env:USERPROFILE\Downloads\scripts\lib\utils.psm1"; url ='https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---utils.psm1'; }
)
try {
# Clear the screen
Clear-Host
# Loop through the array of PowerShell scripts
ForEach ($PowerShellScript in $PowerShellScripts) {
# Get the directory of the PowerShell script
$PowerShellScriptDirectory = Split-Path -Path $PowerShellScript.destination -Parent
# Create the directory if it does not exist
If (-Not (Test-Path -Path $PowerShellScriptDirectory)) {
Write-Host "[Info] Creating directory $PowerShellScriptDirectory" -ForegroundColor Blue
New-Item -Path $PowerShellScriptDirectory -ItemType Directory -Force
}
# If the PowerShell script already exists, remove it
If (Test-Path -Path $PowerShellScript.destination) {
Write-Host "[Info] Removing existing file $($PowerShellScript.destination)" -ForegroundColor Blue
Remove-Item -Path $PowerShellScript.destination -Force
}
# Download the PowerShell script
Write-Host "[Info] Downloading $($PowerShellScript.url) to $($PowerShellScript.destination)" -ForegroundColor Blue
Invoke-WebRequest -Uri $PowerShellScript.url -OutFile $PowerShellScript.destination
}
# Execute the first PowerShell script in the foreground
Write-Host "[Info] Executing $($PowerShellScripts[0].destination)" -ForegroundColor Blue
Start-Process -FilePath "powershell.exe" -NoNewWindow -Wait -ArgumentList "-NoProfile -File `"$($PowerShellScripts[0].destination)`""
} catch {
Write-Host "[Error] $($_.Exception.Message)" -ForegroundColor Red
Read-Host -Prompt "Press any key to continue..."
}
# Title: Bootstrap script of Windows 11 Sandbox deployment script
# Author: B. van Wetten
# Last Update: 2024-01-10
#
# Prerequisites: See: https://gist.github.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df#file-provisioning-sandbox-md
#
# Download only: Invoke-WebRequest -Uri https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---bootstrap_sandbox.ps1
# Usage: PowerShell.exe -ExecutionPolicy Bypass { Invoke-RestMethod -Uri https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---bootstrap_sandbox.ps1 | Invoke-Expression }
# Start of script
Set-StrictMode -Version 1.0
Set-Variable -Name ProgressPreference -Value 'SilentlyContinue' # Default is "Continue"
Set-Variable -Name ErrorActionPreference -Value 'SilentlyContinue' # Default is "Continue"
$global:SandboxVersion = '1.0.0'
$global:TempFolder = "$env:Temp"
$global:TempFolderRemote = 'C:\Users\WDAGUtilityAccount\AppData\Local\Temp'
$global:SandboxUserProfile = "$env:UserProfile"
$global:SandboxUserProfileRemote = 'C:\Users\WDAGUtilityAccount'
$global:SandboxPath = "$env:UserProfile\.sandbox"
$global:SandboxPathRemote = 'C:\Users\WDAGUtilityAccount\Desktop\Sandbox'
$global:SandboxScript = "$global:SandboxPath\.setup\sandbox.ps1"
$global:SandboxScriptRemote = "$global:SandboxPathRemote\.setup\sandbox.ps1"
$global:SandboxScriptsFolder = "$global:SandboxPath\.setup\scripts"
$global:SandboxScriptsFolderRemote = "$global:SandboxPathRemote\.setup\scripts"
$global:SandboxLibFolder = "$global:SandboxPath\.setup\scripts\lib"
$global:SandboxLibFolderRemote = "$global:SandboxPathRemote\.setup\scripts\lib"
$global:errorOccurred = $true
Write-Host '[Info] Starting bootstrap script' -ForegroundColor Yellow
try {
# Clear the screen
Clear-Host
# Check if Windows Sandbox is installed
try {
# Requires elevation: Get-WindowsOptionalFeature -Online -FeatureName Containers-DisposableClientVM -ErrorAction Stop | Out-Null
Get-Command -Name WindowsSandbox -ErrorAction Stop | Out-Null
Write-Host ('[OK] Windows Sandbox Feature is installed, continuing...') -ForegroundColor Green
}
catch {
Throw 'Windows Sandbox Feature is not installed, exiting...'
}
# Check if Sandbox is already running, exit if it is
if ((Get-Process -Name WindowsSandbox -ErrorAction SilentlyContinue).Length -gt 0) {
Write-Host ('[Info] Sandbox is already running, exiting...') -ForegroundColor DarkGray
# Ask if use wants to terminate running Sandbox
$choice = Read-Host -Prompt 'Terminate running Sandbox? [Y/N]'
if ($choice -eq 'Y') {
# Get sandbox process and kill it
$sandboxProcess = Get-Process -Name WindowsSandboxClient -ErrorAction SilentlyContinue
if ($sandboxProcess) {
Write-Host ('[Info] Killing Windows Sandbox process...') -ForegroundColor DarkGray
$sandboxProcess | Stop-Process -Force:$true -Confirm:$false
}
}
else {
Write-Host ('[Info] Exiting...') -ForegroundColor DarkGray
}
exit 0
}
# Create a $global:SandboxPath/.setup folder if it does not exist
if (-not (Test-Path -Path $global:SandboxPath\.setup)) {
New-Item -ItemType Directory -Path $global:SandboxPath\.setup -Force | Out-Null
Write-Host ("[Info] Created $global:SandboxPath\.setup folder") -ForegroundColor DarkGray
}
# Ensure other required folders exist
New-Item -ItemType Directory -Path "$global:SandboxPath\.scoop\" -Force | Out-Null
New-Item -ItemType Directory -Path "$global:SandboxPath\Downloads" -Force | Out-Null
New-Item -ItemType Directory -Path "$global:SandboxPath\installers\data" -Force | Out-Null
New-Item -ItemType Directory -Path "$global:SandboxPath\.setup\scripts\lib" -Force | Out-Null
# Remove previous sandbox_ip
if (Test-Path -Path "$global:SandboxPath\.setup\sandbox_ip") {
Write-Host ("[Info] Removing previous $global:SandboxPath\.setup\sandbox_ip file...") -ForegroundColor DarkGray
Remove-Item -Path "$global:SandboxPath\.setup\sandbox_ip" -Force
}
# Remove previous sandbox_hostname
if (Test-Path -Path "$global:SandboxPath\.setup\sandbox_hostname") {
Write-Host ("[Info] Removing previous $global:SandboxPath\.setup\sandbox_hostname file...") -ForegroundColor DarkGray
Remove-Item -Path "$global:SandboxPath\.setup\sandbox_hostname" -Force
}
# Create a temporary Sandbox.wsb file
$wsb = @"
<Configuration>
<vGPU>Enable</vGPU>
<Networking>Default</Networking>
<AudioInput>Disable</AudioInput>
<VideoInput>Disable</VideoInput>
<PrinterRedirection>Disable</PrinterRedirection>
<ClipboardRedirection>Default</ClipboardRedirection>
<ProtectedClient>Disable</ProtectedClient>
<MappedFolders>
<MappedFolder>
<HostFolder>$global:SandboxUserProfile\.ssh</HostFolder>
<SandboxFolder>$global:SandboxPathRemote\.setup\.ssh</SandboxFolder>
<ReadOnly>true</ReadOnly>
</MappedFolder>
<MappedFolder>
<HostFolder>$global:SandboxPath</HostFolder>
<SandboxFolder>$global:SandboxPathRemote</SandboxFolder>
<ReadOnly>False</ReadOnly>
</MappedFolder>
</MappedFolders>
<LogonCommand>
<Command>powershell.exe -NoProfile -ExecutionPolicy Unrestricted -File "$global:SandboxScriptRemote"</Command>
</LogonCommand>
</Configuration>
"@
$wsb | Out-File "$global:TempFolder\Sandbox.wsb" -Force -Confirm:$false
# Create the sandbox.ps1 script
# Logging can be found in C:\Users\WDAGUtilityAccount\Desktop\Sandbox\.setup\sandbox_transcript
$sandbox = @"
# Start of script
param(
[switch]`$LogTranscript
)
`$scriptName = `$($MyInvocation.MyCommand.Name).Replace('.ps1', '')
`$transcriptFile = "$PSScriptRoot\`${scriptName}_transcript"
if (`$LogTranscript) {
Start-Transcript -Path `$transcriptFile
}
Set-Variable -Name ProgressPreference -Value 'SilentlyContinue' # Default is "Continue"
Set-Variable -Name ErrorActionPreference -Value 'SilentlyContinue' # Default is "Continue"
try {
# Modify local user account WDAGUtilityAccount with password "sandbox"
Write-Host ('[Info] Modifying local user account WDAGUtilityAccount with password "sandbox"...') -ForegroundColor DarkGray
Set-LocalUser -Name WDAGUtilityAccount -PasswordNeverExpires 1 -Password ("sandbox" | ConvertTo-SecureString -AsPlainText -Force)
# Check if 'install.ps1' already exists in $global:SandboxPathRemote\.scoop
if (-not (Test-Path -Path "$global:SandboxPathRemote\.scoop\install.ps1")) {
Write-Host ('[Info] Downloading Scoop install.ps1 script...') -ForegroundColor DarkGray
Invoke-RestMethod -Uri "https://get.scoop.sh" | Out-File -FilePath "$global:SandboxPathRemote\.scoop\install.ps1" -Force
}
Write-Host ('[Info] Installing Scoop...') -ForegroundColor DarkGray
"$global:SandboxPathRemote\.scoop\install.ps1" | Invoke-Expression
Write-Host ('[Info] Configuring Scoop...') -ForegroundColor DarkGray
scoop config cache_path "$global:SandboxPathRemote\.scoop"
scoop config aria2-warning-enabled false
Write-Host ('[Info] Installing Scoop packages...') -ForegroundColor DarkGray
# scoop config use_lessmsi `$true
# scoop install lessmsi
scoop install 7zip aria2 git pwsh
Write-Host ('[Info] Applying Scoop registry settings...') -ForegroundColor DarkGray
reg import "$global:SandboxUserProfileRemote\scoop\apps\git\current\install-context.reg" *> `$null
reg import "$global:SandboxUserProfileRemote\scoop\apps\7zip\current\install-context.reg" *> `$null
reg import "$global:SandboxUserProfileRemote\scoop\apps\git\current\install-file-associations.reg" *> `$null
# Set ExecutionPolicy to Unrestricted
Write-Host ('[Info] Setting ExecutionPolicy to Unrestricted...') -ForegroundColor DarkGray
powershell -Command "Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope LocalMachine -Force"
pwsh -Command "Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope LocalMachine -Force"
# Set network category to Private
Write-Host ("[Info] Setting interface '`$(Get-NetConnectionProfile).InterfaceAlias' - NetworkCategory to Public...") -ForegroundColor DarkGray
Set-NetConnectionProfile -InterfaceAlias `$(Get-NetConnectionProfile).InterfaceAlias -NetworkCategory "Public" | Out-Null
# Enable PowerShell remoting
Write-Host ('[Info] Enabling PowerShell remoting...') -ForegroundColor DarkGray
Enable-PSRemoting -SkipNetworkProfileCheck -Force | Out-Null
# Download Powershell scripts from GitHub
[hashtable[]] `$PowerShellScripts = @(
@{ destination = "$global:SandboxPathRemote\.setup\scripts\sandbox_configure.ps1"; url ='https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---sandbox_configure.ps1'; }
@{ destination = "$global:SandboxPathRemote\.setup\scripts\ps-aliases.ps1"; url ='https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---ps-aliases.ps1'; }
@{ destination = "$global:SandboxPathRemote\.setup\scripts\lib\utils.psm1"; url ='https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---utils.psm1'; }
@{ destination = "$global:SandboxPathRemote\installers\data\vscode_user_data"; url ='https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---vscode_user_data'; }
@{ destination = "$global:SandboxPathRemote\installers\Install development tools.ps1"; url ='https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---sandbox-install-development-tools.ps1'; }
@{ destination = "$global:SandboxPathRemote\installers\Install Brave.ps1"; url ='https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---sandbox-install-brave.ps1'; }
@{ destination = "$global:SandboxPathRemote\installers\Install Telegram.ps1"; url ='https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---sandbox-install-telegram.ps1'; }
@{ destination = "$global:SandboxPathRemote\installers\Install Wireguard.ps1"; url ='https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---sandbox-install-wireguard.ps1'; }
@{ destination = "$global:SandboxPathRemote\installers\Install BlueStacks.ps1"; url ='https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---sandbox-install-bluestacks.ps1'; }
@{ destination = "$global:SandboxPathRemote\installers\Install Audacity.ps1"; url ='https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---sandbox-install-audacity.ps1'; }
@{ destination = "$global:SandboxPathRemote\installers\Install Inkscape.ps1"; url ='https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---sandbox-install-inkscape.ps1'; }
@{ destination = "$global:SandboxPathRemote\installers\Install Scratch 3.ps1"; url ='https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---sandbox-install-scratch.ps1'; }
@{ destination = "$global:SandboxPathRemote\installers\Install Windows Terminal.ps1"; url ='https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---sandbox-install-windows-terminal.ps1'; }
@{ destination = "$global:SandboxPathRemote\installers\Install Apps - Codux.ps1"; url ='https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---sandbox-install-app-codux.ps1'; }
@{ destination = "$global:SandboxPathRemote\installers\Install Apps - LibreCAD.ps1"; url ='https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---sandbox-install-app-librecad.ps1'; }
)
# Create symbolic link to Downloads folder
Write-Host ('[Info] Creating symbolic link to Downloads folder...') -ForegroundColor DarkGray
Remove-Item -Path "$global:SandboxUserProfileRemote\Downloads" -Force -Recurse -ErrorAction SilentlyContinue
New-Item -ItemType SymbolicLink -Path "$global:SandboxUserProfileRemote\Downloads" -Target "$global:SandboxPathRemote\Downloads" -Force | Out-Null
# Store current ip address of Sandbox in $global:SandboxPathRemote\.setup\sandbox_ip
Write-Host ('[Info] Storing current Sandbox IP address in $global:SandboxPathRemote\.setup\sandbox_ip...') -ForegroundColor DarkGray
`$ip = (Get-NetIPAddress -InterfaceAlias Ethernet -AddressFamily IPv4).IPAddress
`$ip | Out-File "$global:SandboxPathRemote\.setup\sandbox_ip" -Force
# Store current computer name of Sandbox in $global:SandboxPathRemote\.setup\sandbox_hostname
Write-Host ('[Info] Storing current Sandbox computer name in $global:SandboxPathRemote\.setup\sandbox_hostname...') -ForegroundColor DarkGray
[Net.Dns]::GetHostEntry("`$ip").HostName | Out-File "$global:SandboxPathRemote\.setup\sandbox_hostname" -Force
# `$env:ComputerName | Out-File "$global:SandboxPathRemote\.setup\sandbox_hostname" -Force
# Copy .ssh folder to C:\Users\WDAGUtilityAccount and create symbolic link
Write-Host ('[Info] Attempting to copy .ssh folder to $global:SandboxUserProfileRemote and creating symbolic link...') -ForegroundColor DarkGray
Copy-Item -Path "$global:SandboxPathRemote\.setup\.ssh" -Destination "$global:SandboxUserProfileRemote" -Recurse -Force -ErrorAction SilentlyContinue
# Create symbolic link to .gitconfig file
Write-Host ('[Info] Attemptint to create symbolic link in $global:SandboxUserProfileRemote to $global:SandboxPathRemote\.setup\.gitconfig file...') -ForegroundColor DarkGray
Copy-Item -Path "$global:SandboxPathRemote\.setup\.gitconfig" -Destination "$global:SandboxUserProfileRemote" -Force -ErrorAction SilentlyContinue
# Download PowerShell scripts from GitHub
Write-Host ('[Info] Downloading PowerShell scripts from GitHub...') -ForegroundColor DarkGray
`$PowerShellScripts | ForEach-Object {
# Download PowerShell scripts from GitHub
Invoke-WebRequest -Uri `$_.url -OutFile `$_.destination
#
# Only required when starting script with PowerShell 5.0
#
# Fix a problem with Powershell 5.0 and UTF-8 without BOM
# See: https://stackoverflow.com/a/5596984/1654717
# `$raw = Get-Content -Raw `$_.destination
# `$encoded = New-Object System.Text.UTF8Encoding `$False
# [IO.File]::WriteAllLines(`$_.destination, `$raw, `$encoded)
}
# <?xml version="1.0" encoding="utf-8"?>
# <LayoutModificationTemplate
# xmlns="http://schemas.microsoft.com/Start/2014/LayoutModification"
# xmlns:defaultlayout="http://schemas.microsoft.com/Start/2014/FullDefaultLayout"
# xmlns:start="http://schemas.microsoft.com/Start/2014/StartLayout"
# xmlns:taskbar="http://schemas.microsoft.com/Start/2014/TaskbarLayout"
# Version="1">
# <CustomTaskbarLayoutCollection PinListPlacement="Replace">
# <defaultlayout:TaskbarLayout>
# <taskbar:TaskbarPinList>
# <taskbar:DesktopApp DesktopApplicationID="Microsoft.Windows.Explorer" />
# <taskbar:DesktopApp DesktopApplicationLinkPath="%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\Microsoft Edge.lnk" />
# </taskbar:TaskbarPinList>
# </defaultlayout:TaskbarLayout>
# </CustomTaskbarLayoutCollection>
# </LayoutModificationTemplate>
Write-Host '[Info] Creating taskbar layout' -ForegroundColor Blue
`$dataFolder = "`${env:UserProfile}\Desktop\Sandbox\installers\data"
`$taskbarLayoutModificationFileData = 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjxMYXlvdXRNb2RpZmljYXRpb25UZW1wbGF0ZQ0KICAgIHhtbG5zPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL1N0YXJ0LzIwMTQvTGF5b3V0TW9kaWZpY2F0aW9uIg0KICAgIHhtbG5zOmRlZmF1bHRsYXlvdXQ9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vU3RhcnQvMjAxNC9GdWxsRGVmYXVsdExheW91dCINCiAgICB4bWxuczpzdGFydD0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS9TdGFydC8yMDE0L1N0YXJ0TGF5b3V0Ig0KICAgIHhtbG5zOnRhc2tiYXI9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vU3RhcnQvMjAxNC9UYXNrYmFyTGF5b3V0Ig0KICAgIFZlcnNpb249IjEiPg0KICA8Q3VzdG9tVGFza2JhckxheW91dENvbGxlY3Rpb24gUGluTGlzdFBsYWNlbWVudD0iUmVwbGFjZSI+DQogICAgPGRlZmF1bHRsYXlvdXQ6VGFza2JhckxheW91dD4NCiAgICAgIDx0YXNrYmFyOlRhc2tiYXJQaW5MaXN0Pg0KICAgICAgICA8dGFza2JhcjpEZXNrdG9wQXBwIERlc2t0b3BBcHBsaWNhdGlvbklEPSJNaWNyb3NvZnQuV2luZG93cy5FeHBsb3JlciIgLz4NCiAgICAgICAgPHRhc2tiYXI6RGVza3RvcEFwcCBEZXNrdG9wQXBwbGljYXRpb25MaW5rUGF0aD0iJVBST0dSQU1EQVRBJVxNaWNyb3NvZnRcV2luZG93c1xTdGFydCBNZW51XFByb2dyYW1zXE1pY3Jvc29mdCBFZGdlLmxuayIgLz4NCiAgICAgIDwvdGFza2JhcjpUYXNrYmFyUGluTGlzdD4NCiAgICA8L2RlZmF1bHRsYXlvdXQ6VGFza2JhckxheW91dD4NCiA8L0N1c3RvbVRhc2tiYXJMYXlvdXRDb2xsZWN0aW9uPg0KPC9MYXlvdXRNb2RpZmljYXRpb25UZW1wbGF0ZT4NCg=='
try {
`$taskbarLayoutModificationFile = "`$dataFolder\TaskbarLayoutModification.xml"
if (!(Test-Path `$dataFolder)) {
# Create directory
New-Item `$dataFolder -ItemType Directory -Force | Out-Null
}
Write-Host "[Info] Creating 'TaskbarLayoutModification.xml' file" -ForegroundColor Blue
try {
# Decode the base64 string
`$decodedBytes = [System.Convert]::FromBase64String(`$taskbarLayoutModificationFileData)
# Ensure the directory exists
`$directoryPath = [System.IO.Path]::GetDirectoryName(`$taskbarLayoutModificationFile)
if (-not (Test-Path `$directoryPath)) {
New-Item -Path `$directoryPath -ItemType Directory -Force | Out-Null
}
[System.IO.File]::WriteAllBytes(`$taskbarLayoutModificationFile, `$decodedBytes);
}
catch {
Write-Host "[Error] Failed to write file from base64 string. Error: `$_" -ForegroundColor Red
}
New-Item -Path "HKCU:\Software\Policies\Microsoft\Windows\Explorer" -Force > `$null
New-Item -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Group Policy Objects\{E0CC53D9-2FFF-4D2E-BECB-333B41615D7E}User\Software\Policies\Microsoft\Windows\Explorer" -Force > `$null
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Group Policy Objects\{E0CC53D9-2FFF-4D2E-BECB-333B41615D7E}User\Software\Policies\Microsoft\Windows\Explorer" -Name "LockedStartLayout" -Value 1 -Type "DWord"
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Group Policy Objects\{E0CC53D9-2FFF-4D2E-BECB-333B41615D7E}User\Software\Policies\Microsoft\Windows\Explorer" -Name "ReapplyStartLayoutEveryLogon" -Value 1 -Type "DWord"
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Group Policy Objects\{E0CC53D9-2FFF-4D2E-BECB-333B41615D7E}User\Software\Policies\Microsoft\Windows\Explorer" -Name "StartLayoutFile" -Value "`$taskbarLayoutModificationFile" -Type "String"
Set-ItemProperty -Path "HKCU:\Software\Policies\Microsoft\Windows\Explorer" -Name "LockedStartLayout" -Value 1 -Type "DWord"
Set-ItemProperty -Path "HKCU:\Software\Policies\Microsoft\Windows\Explorer" -Name "ReapplyStartLayoutEveryLogon" -Value 1 -Type "DWord"
Set-ItemProperty -Path "HKCU:\Software\Policies\Microsoft\Windows\Explorer" -Name "StartLayoutFile" -Value "`$taskbarLayoutModificationFile" -Type "String"
}
catch {
Write-Host "[Error] Failed to set taskbar layout. Error: `$_" -ForegroundColor Red
}
Get-Process -Name Explorer | Stop-Process -Force
Start-Sleep -Seconds 3
try {
`$p = Get-Process -Name Explorer -ErrorAction Stop
}
catch {
Try {
Start-Process explorer.exe
}
Catch {
# This should never be called
Throw `$_
}
}
} catch {
Write-Host "[Error] `$(`$_.Exception.Message)" -ForegroundColor Red
exit 1
}
# Execute PowerShell script
Write-Host ("[Info] Executing PowerShell script...") -ForegroundColor DarkGray
$global:SandboxUserProfileRemote\scoop\apps\pwsh\current\pwsh.exe -File "$global:SandboxPathRemote\.setup\scripts\sandbox_configure.ps1" -Wait
Write-Host ('[Info] Done...') -ForegroundColor DarkGray
Stop-Transcript
"@
$sandbox | Out-File -FilePath $global:SandboxScript -Force
$constants = @"
`$downloadFolder = (New-Object -ComObject Shell.Application).NameSpace('shell:Downloads').Self.Path
`$sandboxFolder = "`${env:UserProfile}\Desktop\Sandbox"
`$scoopFolder = "`$sandboxFolder\.scoop"
`$gistsFolder = "`$sandboxFolder\gists"
`$installersFolder = "`$sandboxFolder\installers"
`$dataFolder = "`$installersFolder\data"
Export-ModuleMember -Variable downloadFolder, sandboxFolder, scoopFolder, gistsFolder, installersFolder, dataFolder
"@
$constants | Out-File -FilePath $global:SandboxLibFolder\constants.psm1 -Force
# Start Windows Sandbox using the Sandbox.wsb file
Write-Host ('[Info] Starting Windows Sandbox...') -ForegroundColor DarkGray
Start-Process -FilePath $(Get-Command -Name WindowsSandbox).Source -ArgumentList "$global:TempFolder\Sandbox.wsb" | Out-Null
# Wait for installation of OpenSSH server and creation of sandbox_ip file
$interval = 0
while (-not (Test-Path -Path $global:SandboxPath\.setup\sandbox_ip)) {
Start-Sleep -Seconds 1
# Perform modulo on $interval to check if it is a multiple of 5
if (($interval++ % 15) -eq 0) {
Write-Host ("[Info] Waiting for creation of $global:SandboxPath\.setup\sandbox_ip file... ($interval seconds)") -ForegroundColor DarkGray
}
if ($interval -gt 90) {
Throw "Error creating $global:SandboxPath\.setup\sandbox_ip file, exiting..."
}
}
# Retrieve current ip address of Sandbox from $global:SandboxPath\.setup\sandbox_ip
$sandbox_ip = Get-Content $global:SandboxPath\.setup\sandbox_ip
# If ip address is not in the 10.0.0.0/8 range or the 172.16.0.0/12 range or the 192.168.0.0/16 range, exit
if (($sandbox_ip -notmatch '^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$') -and ($sandbox_ip -notmatch '^172\.[1-3]\d\.\d{1,3}\.\d{1,3}$') -and ($sandbox_ip -notmatch '^192\.168\.\d{1,3}\.\d{1,3}$')) {
Throw 'Error retrieving Sandbox IP address, exiting...'
}
Write-Host ('[OK] Installation done...') -ForegroundColor Green
$global:errorOccurred = $false
}
catch {
Write-Host "[Error] $($_.Exception.Message)" -ForegroundColor Red
# Get sandbox process and kill it
$sandboxProcess = Get-Process -Name WindowsSandboxClient -ErrorAction SilentlyContinue
if ($sandboxProcess) {
Write-Host ('[Info] Killing Windows Sandbox process...') -ForegroundColor DarkGray
$sandboxProcess | Stop-Process -Force:$true -Confirm:$false
}
}
finally {
if ($global:errorOccurred) {
# Wait for keypress
Write-Host ('[Done] Press any key to exit...') -ForegroundColor Yellow
$null = $host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
exit 1
}
else {
$timeout = 3
# Wait for keypress or continue after 10 seconds
Write-Host ('[Done] Press any key to exit or wait 3 seconds...') -ForegroundColor Green
$timer = New-Object System.Diagnostics.Stopwatch
$timer.Start()
while (-not [Console]::KeyAvailable -and ($timeout -eq 0 -or $timer.Elapsed.Seconds -lt $timeout)) {
Start-Sleep -Milliseconds 50
}
if ([Console]::KeyAvailable) {
[void][Console]::ReadKey($true)
}
exit 0
}
}
https://gist.githubusercontent.com/QNimbus/ff3ea6edad2d62a8f56d7029d62a118a/raw/.gitconfig%20(windows)
out=.gitconfig
https://gist.githubusercontent.com/QNimbus/e6e974f95217b109789f4b8f9730800b/raw/config%20(windows)
out=.ssh/config
https://gist.githubusercontent.com/QNimbus/41b14daabd04c6bd2f24c12c1a087904/raw/settings.sandbox.json
out=windows-terminal-settings.json
<#
.SYNOPSIS
This script sets up a PowerShell profile for running scripts inside the Windows Sandbox VM.
.DESCRIPTION
The script sets up a PowerShell profile with aliases and functions for interacting with a 1Password account inside the Windows Sandbox VM. It checks if the script is running inside the Windows Sandbox VM, if the 1Password CLI is installed, and if the sandbox folder exists. It retrieves the IP address of the sandbox and adds it to the trusted hosts list if necessary. It then retrieves the 1Password account credentials and creates a PowerShell profile with aliases and a function for adding the 1Password account to the sandbox terminal session.
.PARAMETER VaultName
The name of the 1Password vault. Default is 'Private'.
.PARAMETER AccountName
The name of the 1Password account. Default is '1Password'.
.PARAMETER SandboxFolder
The path to the sandbox folder. Default is '$env:UserProfile\.sandbox'.
.PARAMETER Transcript
A switch parameter to enable transcript logging. Default is $false.
.EXAMPLE
.\ps-aliases.ps1 -VaultName 'Private' -AccountName '1Password' -SandboxFolder 'C:\Sandbox' -Transcript
This example sets up the PowerShell profile with the specified vault name, account name, sandbox folder, and enables transcript logging.
.NOTES
This script requires the 1Password CLI to be installed and the Windows Sandbox VM to be running.
#>
Param (
[Parameter(Mandatory = $false)]
[Alias('vn')]
[string]$VaultName = 'Private',
[Parameter(Mandatory = $false)]
[Alias('an')]
[string]$AccountName = '1Password',
[Parameter(Mandatory = $false)]
[Alias('sf')]
[string]$SandboxFolder = "$env:UserProfile\.sandbox",
[Parameter(Mandatory = $false)]
[Alias('t')]
[Switch]$Transcript = $false
)
$scriptName = $($MyInvocation.MyCommand.Name).Replace('.ps1', '')
$transcriptFile = "$PSScriptRoot\${scriptName}_transcript"
if ($Transcript) { Start-Transcript -Path $transcriptFile }
Set-StrictMode -Version 1.0
Set-Variable -Name ProgressPreference -Value 'SilentlyContinue' # Default is "Continue"
Set-Variable -Name ErrorActionPreference -Value 'SilentlyContinue' # Default is "Continue"
try {
# Check if we run inside the Windows Sandbox VM
if (-not (Get-Process -Name 'WindowsSandbox')) { throw 'Script is *not* intended to run inside the Windows Sandbox VM' }
if (-not (Get-Command -Name 'op' -ErrorAction SilentlyContinue)) { throw '1Password-CLI not installed' }
if (-not (Test-Path $SandboxFolder)) { throw "Sandbox folder '$SandboxFolder' not found" }
$ip = (Get-Content -Path ..\sandbox_ip -ErrorAction SilentlyContinue)
if (-not $ip) { throw 'Unable to retrieve sandbox IP address' }
# Check if the ip address is already in the trusted hosts list
$trustedHosts = Get-Item -Path WSMan:\localhost\Client\TrustedHosts -ErrorAction SilentlyContinue
if ($trustedHosts -and -not( $trustedHosts.Value -match $ip)) {
Write-Host("[Info] Adding sandbox IP address ($ip) to trusted hosts...")
Start-Process -FilePath powershell.exe -WindowStyle Hidden -Verb RunAs -Wait -ArgumentList "-Command Set-Item -Path WSMan:\localhost\Client\TrustedHosts -Value $ip -Force"
}
$sandboxUser = 'WDAGUtilityAccount'
$sandboxPassword = "sandbox" | ConvertTo-SecureString -AsPlainText -Force
[PSCredential] $credential = New-Object System.Management.Automation.PSCredential($sandboxUser, $sandboxPassword)
$session = New-PSSession -ComputerName $ip -Credential $credential
Write-Host('[Info] Retrieving 1Password account credentials...')
# Redirect stderr to null to suppress error messages
Write-Host("[Info] Retrieving 1Password sign-in address: op://$VaultName/$AccountName/website")
$opWebsite = ([Uri](op read "op://$VaultName/$AccountName/website" 2>$null)).Authority
Write-Host("[Info] Retrieving 1Password username: op://$VaultName/$AccountName/username")
$opSecretKey = op read "op://$VaultName/$AccountName/secret key" 2>$null
Write-Host("[Info] Retrieving 1Password secret key: op://$VaultName/$AccountName/secret key")
$opUsername = op read "op://$VaultName/$AccountName/username" 2>$null
if (-not $opWebsite -or -not $opUsername -or -not $opSecretKey) { throw 'Unable to retrieve website, username, or secret key from 1Password account' }
$script = @"
Clear-Host
Write-Host("[Notice] Adding 1Password utility scripts to PowerShell profile...`n") -ForegroundColor Yellow
# Display available aliases and usage info
Write-Host("[Info] Available aliases:") -ForegroundColor White
Write-Host("- op! : Log in to 1Password account") -ForegroundColor White
Write-Host("`n")
<#
.SYNOPSIS
This function adds an account using the 'op' command-line tool.
.DESCRIPTION
The 'opup' function attempts to add an account using the 'op' command-line tool. It provides the necessary parameters for signing in, including the shorthand, address, email, and secret key.
.PARAMETER None
.EXAMPLE
opup
#>
function 1Password-Up() {
try {
op account add --signin --shorthand sandbox --address '$opWebsite' --email '$opUsername' --secret-key '$opSecretKey' | Invoke-Expression
} catch {
Write-Host("[Error] $_") -ForegroundColor Red
}
}
Set-Alias -Name "op!" -Value "1Password-Up" -Description "Add 1Password account to sandbox terminal session" -Option AllScope
"@
$script | Out-File -FilePath .\Profile.ps1 -Force
$userProfile = Invoke-Command -Session $session -ScriptBlock { $env:UserProfile }
Copy-Item .\Profile.ps1 -Destination "$userProfile\scoop\apps\pwsh\current\Profile.ps1" -ToSession $session -ErrorAction Stop
}
catch {
Write-Host "[Error] $_" -ForegroundColor Red
}
finally {
Remove-Item -Path .\Profile.ps1 -Force
if ($session) { Remove-PSSession -Session $session -ErrorAction SilentlyContinue }
if ($Transcript) { Stop-Transcript }
$timer = New-Object System.Diagnostics.Stopwatch
$timer.Start()
$timeout = 3
Write-Host ('[Done] Press any key to exit or wait 3 seconds...') -ForegroundColor Green
while (-not [Console]::KeyAvailable -and ($timeout -eq 0 -or $timer.Elapsed.Seconds -lt $timeout)) {
Start-Sleep -Milliseconds 50
}
if ([Console]::KeyAvailable) {
[void][Console]::ReadKey($true)
}
exit 0
}
# Title: Windows 11 Sandbox deployment script - Install Codux application
# Author: B. van Wetten
# Last Update: 2024-01-18
# Start of script
Using module ..\.setup\scripts\lib\utils.psm1
Using module ..\.setup\scripts\lib\constants.psm1
param(
[switch]$LogTranscript
)
$scriptName = $($MyInvocation.MyCommand.Name).Replace('.ps1', '')
$transcriptFile = "$PSScriptRoot\${scriptName}_transcript"
if ($LogTranscript) {
Start-Transcript -Path $transcriptFile
}
Set-StrictMode -Version 1.0
Set-Variable -Name ProgressPreference -Value 'SilentlyContinue' # Default is "Continue"
Set-Variable -Name ErrorActionPreference -Value 'SilentlyContinue' # Default is "Continue"
try {
# Download and install Codux application
$coduxUrl = 'https://github.com/wixplosives/codux-versions/releases/download/15.17.2/Codux-15.17.2.x64.exe'
$coduxFile = "$downloadFolder\Codux-15.17.2.x64.exe"
if (-not (Test-Path $coduxFile)) {
Write-Host '[Info] Downloading Codux application' -ForegroundColor Blue
Invoke-WebRequest -Uri $coduxUrl -OutFile $coduxFile
}
Write-Host '[Info] Installing Codux application' -ForegroundColor Blue
Start-Process -FilePath $coduxFile -ArgumentList '/S /AllUsers'
}
catch {
Write-Host "[Error] $_" -ForegroundColor Red
}
# Title: Windows 11 Sandbox deployment script - Install LibreCAD application
# Author: B. van Wetten
# Last Update: 2024-01-30
# Start of script
Using module ..\.setup\scripts\lib\utils.psm1
Using module ..\.setup\scripts\lib\constants.psm1
param(
[switch]$LogTranscript
)
$scriptName = $($MyInvocation.MyCommand.Name).Replace('.ps1', '')
$transcriptFile = "$PSScriptRoot\${scriptName}_transcript"
if ($LogTranscript) {
Start-Transcript -Path $transcriptFile
}
Set-StrictMode -Version 1.0
Set-Variable -Name ProgressPreference -Value 'SilentlyContinue' # Default is "Continue"
Set-Variable -Name ErrorActionPreference -Value 'SilentlyContinue' # Default is "Continue"
try {
# Download and install LibreCAD application
$downloadUrl = 'https://github.com/LibreCAD/LibreCAD/releases/download/2.2.0.2/LibreCAD-Installer-2.2.0.2.exe'
$downloadFile = "$downloadFolder\LibreCAD-Installer-2.2.0.2.exe"
if (-not (Test-Path $downloadFile)) {
Write-Host '[Info] Downloading LibrCAD application' -ForegroundColor Blue
Invoke-WebRequest -Uri $downloadUrl -OutFile $downloadFile
}
Write-Host '[Info] Installing LibreCAD application' -ForegroundColor Blue
Start-Process -FilePath $downloadFile -ArgumentList '/S /AllUsers'
}
catch {
Write-Host "[Error] $_" -ForegroundColor Red
}
# Title: Windows 11 Sandbox deployment script - Install Audacity
# Author: B. van Wetten
# Last Update: 2024-01-18
# Start of script
Using module ..\.setup\scripts\lib\utils.psm1
Using module ..\.setup\scripts\lib\constants.psm1
param(
[switch]$LogTranscript
)
$scriptName = $($MyInvocation.MyCommand.Name).Replace('.ps1', '')
$transcriptFile = "$PSScriptRoot\${scriptName}_transcript"
if ($LogTranscript) {
Start-Transcript -Path $transcriptFile
}
Set-StrictMode -Version 1.0
Set-Variable -Name ProgressPreference -Value 'SilentlyContinue' # Default is "Continue"
Set-Variable -Name ErrorActionPreference -Value 'SilentlyContinue' # Default is "Continue"
# Example usage
$filePath = 'Path\To\TaskbarLayoutModification.xml'
$appPath = '%APPDATA%\Microsoft\Windows\Start Menu\Programs\YourApp.lnk'
try {
# Check that 'scoop' is installed
if (-not (Get-Command -Name scoop)) {
throw 'Scoop is not installed'
}
$sources = @('extras')
$pkgs = @(
'audacity'
)
# Add sources and install packages
$sources | ForEach-Object {
Write-Host "[Scoop] Adding scoop bucket '$_'..." -ForegroundColor DarkGray
scoop bucket add $_ *> $null
}
$pkgs | ForEach-Object {
Write-Host "[Scoop] Installing scoop package '$_'..." -ForegroundColor DarkGray
scoop install $_ *> $null
}
Write-Host "[Info] Appending 'Audacity' shortcut to taskbar layout" -ForegroundColor Blue
$xmlFilePath = "$dataFolder\TaskbarLayoutModification.xml"
[Utils]::AddAppToTaskbarPinList($xmlFilePath, '%APPDATA%\Microsoft\Windows\Start Menu\Programs\Scoop Apps\Audacity.lnk')
[Utils]::RestartExplorer()
}
catch {
Write-Host "[Error] $_" -ForegroundColor Red
}
# Title: Windows 11 Sandbox deployment script - Install BlueStacks
# Author: B. van Wetten
# Last Update: 2024-03-10
# Start of script
Using module ..\.setup\scripts\lib\utils.psm1
Using module ..\.setup\scripts\lib\constants.psm1
param(
[switch]$LogTranscript
)
$scriptName = $($MyInvocation.MyCommand.Name).Replace('.ps1', '')
$transcriptFile = "$PSScriptRoot\${scriptName}_transcript"
if ($LogTranscript) {
Start-Transcript -Path $transcriptFile
}
Set-StrictMode -Version 1.0
Set-Variable -Name ProgressPreference -Value 'SilentlyContinue' # Default is "Continue"
Set-Variable -Name ErrorActionPreference -Value 'SilentlyContinue' # Default is "Continue"
try {
# Check that 'scoop' is installed
if (-not (Get-Command -Name scoop)) {
throw 'Scoop is not installed'
}
$sources = @('nonportable')
$pkgs = @(
'bluestacks-np'
)
# Add sources and install packages
$sources | ForEach-Object {
Write-Host "[Scoop] Adding scoop bucket '$_'..." -ForegroundColor DarkGray
scoop bucket add $_ *> $null
}
$pkgs | ForEach-Object {
Write-Host "[Scoop] Installing scoop package '$_'..." -ForegroundColor DarkGray
scoop install $_ *> $null
}
Write-Host "[Info] Appending 'BlueStacks' shortcut to taskbar layout" -ForegroundColor Blue
$xmlFilePath = "$dataFolder\TaskbarLayoutModification.xml"
[Utils]::AddAppToTaskbarPinList($xmlFilePath, "%CSIDL_COMMON_PROGRAMS%\Microsoft\Windows\Start Menu\Programs\BlueStacks 5.lnk")
[Utils]::RestartExplorer()
}
catch {
Write-Host "[Error] $_" -ForegroundColor Red
}
# Title: Windows 11 Sandbox deployment script - Install Brave
# Author: B. van Wetten
# Last Update: 2024-01-18
# Start of script
Using module ..\.setup\scripts\lib\utils.psm1
Using module ..\.setup\scripts\lib\constants.psm1
param(
[switch]$LogTranscript
)
$scriptName = $($MyInvocation.MyCommand.Name).Replace('.ps1', '')
$transcriptFile = "$PSScriptRoot\${scriptName}_transcript"
if ($LogTranscript) {
Start-Transcript -Path $transcriptFile
}
Set-StrictMode -Version 1.0
Set-Variable -Name ProgressPreference -Value 'SilentlyContinue' # Default is "Continue"
Set-Variable -Name ErrorActionPreference -Value 'SilentlyContinue' # Default is "Continue"
try {
# Check that 'scoop' is installed
if (-not (Get-Command -Name scoop)) {
throw 'Scoop is not installed'
}
$sources = @('extras')
$pkgs = @(
'brave'
)
# Add sources and install packages
$sources | ForEach-Object {
Write-Host "[Scoop] Adding scoop bucket '$_'..." -ForegroundColor DarkGray
scoop bucket add $_ *> $null
}
$pkgs | ForEach-Object {
Write-Host "[Scoop] Installing scoop package '$_'..." -ForegroundColor DarkGray
scoop install $_ *> $null
}
Write-Host "[Info] Appending 'Brave' shortcut to taskbar layout" -ForegroundColor Blue
$xmlFilePath = "$dataFolder\TaskbarLayoutModification.xml"
[Utils]::AddAppToTaskbarPinList($xmlFilePath, "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Scoop Apps\Brave.lnk")
[Utils]::RestartExplorer()
}
catch {
Write-Host "[Error] $_" -ForegroundColor Red
}
# Title: Windows 11 Sandbox deployment script - Install development tools
# Author: B. van Wetten
# Last Update: 2024-01-11
# Start of script
Using module ..\.setup\scripts\lib\utils.psm1
Using module ..\.setup\scripts\lib\constants.psm1
param(
[switch]$LogTranscript
)
$scriptName = $($MyInvocation.MyCommand.Name).Replace('.ps1', '')
$transcriptFile = "$PSScriptRoot\${scriptName}_transcript"
if ($LogTranscript) {
Start-Transcript -Path $transcriptFile
}
Set-StrictMode -Version 1.0
Set-Variable -Name ProgressPreference -Value 'SilentlyContinue' # Default is "Continue"
Set-Variable -Name ErrorActionPreference -Value 'SilentlyContinue' # Default is "Continue"
try {
# Check that 'scoop' is installed
if (-not (Get-Command -Name scoop)) {
throw 'Scoop is not installed'
}
$sources = @('extras')
$pkgs = @(
'vscode',
'vcredist2022',
'openssh',
'nodejs',
'python',
'jq',
'1password-cli'
)
# Add sources and install packages
$sources | ForEach-Object {
Write-Host "[Scoop] Adding scoop bucket '$_'..." -ForegroundColor DarkGray
scoop bucket add $_ *> $null
}
$pkgs | ForEach-Object {
Write-Host "[Scoop] Installing scoop package '$_'..." -ForegroundColor DarkGray
scoop install $_ *> $null
}
# VSCode post-installation
Write-Host '[Info] Installing VSCode registry modifications' -ForegroundColor Blue
reg import "${env:USERPROFILE}\scoop\apps\vscode\current\install-associations.reg" *> $null
reg import "${env:USERPROFILE}\scoop\apps\vscode\current\install-context.reg" *> $null
# Restore VSCode settings
# Archive was created with:
# `tar -cf - \
# --exclude=settings.json \
# --exclude=*.backup \
# --exclude=data/user-data/User/snippets \
# --exclude=data/user-data/User/History \
# data/user-data/User | gzip -c | base64 > vscode_user_data`
Write-Host '[Info] Restoring VSCode settings' -ForegroundColor Blue
[Utils]::WriteArchive("$dataFolder\vscode_user_data", "${env:USERPROFILE}\scoop\apps\vscode\current")
# VSCode settings
# {
# "workbench.startupEditor": "none",
# "security.workspace.trust.enabled": false
# }
$vscodeSettings = 'ew0KCSJ3b3JrYmVuY2guc3RhcnR1cEVkaXRvciI6ICJub25lIiwNCgkic2VjdXJpdHkud29ya3NwYWNlLnRydXN0LmVuYWJsZWQiOiBmYWxzZQ0KfQ0K'
$vsCodeSettingsLocation = "${env:USERPROFILE}\scoop\apps\vscode\current\data\user-data\User"
$vsCodeSettingsFile = "$vsCodeSettingsLocation\settings.json"
# Create VSCode settings file
Write-Host '[Info] Creating VSCode settings file' -ForegroundColor Blue
New-Item $vsCodeSettingsLocation -ItemType Directory -Force | Out-Null
[Utils]::WriteFile($vscodeSettings, $vsCodeSettingsFile) | Out-Null
# OpenSSH post-installation
Write-Host '[Info] Running OpenSSH post-install scripts' -ForegroundColor Blue
Start-Process -FilePath powershell.exe -ArgumentList "-ExecutionPolicy Bypass -File ${env:USERPROFILE}\scoop\apps\openssh\current\install-sshd.ps1" -Wait
Start-Service sshd
# Python post-installation
Write-Host '[Info] Installing Python registry modifications' -ForegroundColor Blue
reg import "${env:USERPROFILE}\scoop\apps\python\current\install-pep-514.reg" *> $null
Write-Host "[Info] Appending 'VSCode' shortcut to taskbar layout" -ForegroundColor Blue
$xmlFilePath = "$dataFolder\TaskbarLayoutModification.xml"
[Utils]::AddAppToTaskbarPinList($xmlFilePath, "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Scoop Apps\Visual Studio Code.lnk")
[Utils]::RestartExplorer()
}
catch {
Write-Host "[Error] $_" -ForegroundColor Red
}
# Title: Windows 11 Sandbox deployment script - Install Inkscape
# Author: B. van Wetten
# Last Update: 2024-01-30
# Start of script
Using module ..\.setup\scripts\lib\utils.psm1
Using module ..\.setup\scripts\lib\constants.psm1
param(
[switch]$LogTranscript
)
$scriptName = $($MyInvocation.MyCommand.Name).Replace('.ps1', '')
$transcriptFile = "$PSScriptRoot\${scriptName}_transcript"
if ($LogTranscript) {
Start-Transcript -Path $transcriptFile
}
Set-StrictMode -Version 1.0
Set-Variable -Name ProgressPreference -Value 'SilentlyContinue' # Default is "Continue"
Set-Variable -Name ErrorActionPreference -Value 'SilentlyContinue' # Default is "Continue"
# Example usage
$filePath = 'Path\To\TaskbarLayoutModification.xml'
$appPath = '%APPDATA%\Microsoft\Windows\Start Menu\Programs\YourApp.lnk'
try {
# Check that 'scoop' is installed
if (-not (Get-Command -Name scoop)) {
throw 'Scoop is not installed'
}
$sources = @('extras')
$pkgs = @(
'inkscape'
)
# Add sources and install packages
$sources | ForEach-Object {
Write-Host "[Scoop] Adding scoop bucket '$_'..." -ForegroundColor DarkGray
scoop bucket add $_ *> $null
}
$pkgs | ForEach-Object {
Write-Host "[Scoop] Installing scoop package '$_'..." -ForegroundColor DarkGray
scoop install $_ *> $null
}
Write-Host "[Info] Appending 'Inkscape' shortcut to taskbar layout" -ForegroundColor Blue
$xmlFilePath = "$dataFolder\TaskbarLayoutModification.xml"
[Utils]::AddAppToTaskbarPinList($xmlFilePath, '%APPDATA%\Microsoft\Windows\Start Menu\Programs\Scoop Apps\Inkscape.lnk')
[Utils]::RestartExplorer()
}
catch {
Write-Host "[Error] $_" -ForegroundColor Red
}
# Title: Windows 11 Sandbox deployment script - Install Scratch
# Author: B. van Wetten
# Last Update: 2024-01-20
# Start of script
Using module ..\.setup\scripts\lib\utils.psm1
Using module ..\.setup\scripts\lib\constants.psm1
param(
[switch]$LogTranscript
)
$scriptName = $($MyInvocation.MyCommand.Name).Replace('.ps1', '')
$transcriptFile = "$PSScriptRoot\${scriptName}_transcript"
if ($LogTranscript) {
Start-Transcript -Path $transcriptFile
}
Set-StrictMode -Version 1.0
Set-Variable -Name ProgressPreference -Value 'SilentlyContinue' # Default is "Continue"
Set-Variable -Name ErrorActionPreference -Value 'SilentlyContinue' # Default is "Continue"
try {
# Check that 'scoop' is installed
if (-not (Get-Command -Name scoop)) {
throw 'Scoop is not installed'
}
$sources = @('extras')
$pkgs = @(
'scratch'
)
# Add sources and install packages
$sources | ForEach-Object {
Write-Host "[Scoop] Adding scoop bucket '$_'..." -ForegroundColor DarkGray
scoop bucket add $_ *> $null
}
$pkgs | ForEach-Object {
Write-Host "[Scoop] Installing scoop package '$_'..." -ForegroundColor DarkGray
scoop install $_ *> $null
}
Write-Host "[Info] Appending 'Scratch' shortcut to taskbar layout" -ForegroundColor Blue
$xmlFilePath = "$dataFolder\TaskbarLayoutModification.xml"
[Utils]::AddAppToTaskbarPinList($xmlFilePath, "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Scoop Apps\Scratch 3.lnk")
[Utils]::RestartExplorer()
}
catch {
Write-Host "[Error] $_" -ForegroundColor Red
}
# Title: Windows 11 Sandbox deployment script - Install Telegram
# Author: B. van Wetten
# Last Update: 2024-02-04
# Start of script
Using module ..\.setup\scripts\lib\utils.psm1
Using module ..\.setup\scripts\lib\constants.psm1
param(
[switch]$LogTranscript
)
$scriptName = $($MyInvocation.MyCommand.Name).Replace('.ps1', '')
$transcriptFile = "$PSScriptRoot\${scriptName}_transcript"
if ($LogTranscript) {
Start-Transcript -Path $transcriptFile
}
Set-StrictMode -Version 1.0
Set-Variable -Name ProgressPreference -Value 'SilentlyContinue' # Default is "Continue"
Set-Variable -Name ErrorActionPreference -Value 'SilentlyContinue' # Default is "Continue"
try {
# Check that 'scoop' is installed
if (-not (Get-Command -Name scoop)) {
throw 'Scoop is not installed'
}
$sources = @('extras')
$pkgs = @(
'telegram'
)
# Add sources and install packages
$sources | ForEach-Object {
Write-Host "[Scoop] Adding scoop bucket '$_'..." -ForegroundColor DarkGray
scoop bucket add $_ *> $null
}
$pkgs | ForEach-Object {
Write-Host "[Scoop] Installing scoop package '$_'..." -ForegroundColor DarkGray
scoop install $_ *> $null
}
Write-Host "[Info] Appending 'Telegram' shortcut to taskbar layout" -ForegroundColor Blue
$xmlFilePath = "$dataFolder\TaskbarLayoutModification.xml"
[Utils]::AddAppToTaskbarPinList($xmlFilePath, "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Scoop Apps\Telegram.lnk")
[Utils]::RestartExplorer()
}
catch {
Write-Host "[Error] $_" -ForegroundColor Red
}
# Title: Windows 11 Sandbox deployment script - Install Windows Terminal
# Author: B. van Wetten
# Last Update: 2024-01-18
# Start of script
Using module ..\.setup\scripts\lib\utils.psm1
Using module ..\.setup\scripts\lib\constants.psm1
param(
[switch]$LogTranscript
)
$scriptName = $($MyInvocation.MyCommand.Name).Replace('.ps1', '')
$transcriptFile = "$PSScriptRoot\${scriptName}_transcript"
if ($LogTranscript) {
Start-Transcript -Path $transcriptFile
}
Set-StrictMode -Version 1.0
Set-Variable -Name ProgressPreference -Value 'SilentlyContinue' # Default is "Continue"
Set-Variable -Name ErrorActionPreference -Value 'SilentlyContinue' # Default is "Continue"
try {
# Check that 'scoop' is installed
if (-not (Get-Command -Name scoop)) {
throw 'Scoop is not installed'
}
$sources = @('extras')
$pkgs = @(
'windows-terminal'
)
# Add sources and install packages
$sources | ForEach-Object {
Write-Host "[Scoop] Adding scoop bucket '$_'..." -ForegroundColor DarkGray
scoop bucket add $_ *> $null
}
$pkgs | ForEach-Object {
Write-Host "[Scoop] Installing scoop package '$_'..." -ForegroundColor DarkGray
scoop install $_ *> $null
}
# Windows Terminal post-installation
Write-Host '[Info] Installing Windows Terminal registry modifications' -ForegroundColor Blue
Copy-Item "$gistsFolder\windows-terminal-settings.json" "${env:USERPROFILE}\scoop\apps\windows-terminal\current\settings\settings.json" -Force -ErrorAction SilentlyContinue | Out-Null
reg import "${env:USERPROFILE}\scoop\apps\windows-terminal\current\install-context.reg" *> $null
Write-Host "[Info] Appending 'Windows Terminal' shortcut to taskbar layout" -ForegroundColor Blue
$xmlFilePath = "$dataFolder\TaskbarLayoutModification.xml"
[Utils]::AddAppToTaskbarPinList($xmlFilePath, "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Scoop Apps\Windows Terminal.lnk")
[Utils]::RestartExplorer()
}
catch {
Write-Host "[Error] $_" -ForegroundColor Red
}
# Title: Windows 11 Sandbox deployment script - Install Wireguard
# Author: B. van Wetten
# Last Update: 2024-02-05
# Start of script
Using module ..\.setup\scripts\lib\utils.psm1
Using module ..\.setup\scripts\lib\constants.psm1
param(
[switch]$LogTranscript
)
$scriptName = $($MyInvocation.MyCommand.Name).Replace('.ps1', '')
$transcriptFile = "$PSScriptRoot\${scriptName}_transcript"
if ($LogTranscript) {
Start-Transcript -Path $transcriptFile
}
Set-StrictMode -Version 1.0
Set-Variable -Name ProgressPreference -Value 'SilentlyContinue' # Default is "Continue"
Set-Variable -Name ErrorActionPreference -Value 'SilentlyContinue' # Default is "Continue"
try {
# Check that 'scoop' is installed
if (-not (Get-Command -Name scoop)) {
throw 'Scoop is not installed'
}
$sources = @('nonportable')
$pkgs = @(
'wireguard-np'
)
# Add sources and install packages
$sources | ForEach-Object {
Write-Host "[Scoop] Adding scoop bucket '$_'..." -ForegroundColor DarkGray
scoop bucket add $_ *> $null
}
$pkgs | ForEach-Object {
Write-Host "[Scoop] Installing scoop package '$_'..." -ForegroundColor DarkGray
scoop install $_ *> $null
}
Write-Host "[Info] Appending 'Wireguard' shortcut to taskbar layout" -ForegroundColor Blue
$xmlFilePath = "$dataFolder\TaskbarLayoutModification.xml"
[Utils]::AddAppToTaskbarPinList($xmlFilePath, "%CSIDL_COMMON_PROGRAMS%\Microsoft\Windows\Start Menu\Programs\Wireguard.lnk")
[Utils]::RestartExplorer()
}
catch {
Write-Host "[Error] $_" -ForegroundColor Red
}
# Title: Windows 11 Sandbox deployment script
# Author: B. van Wetten
# Description: Deployment script Windows Sandbox
# Last Update: 2024-01-18
# Start of script
Using module .\lib\utils.psm1
Using module .\lib\constants.psm1
param(
[switch]$LogTranscript
)
$scriptName = $($MyInvocation.MyCommand.Name).Replace('.ps1', '')
$transcriptFile = "$PSScriptRoot\${scriptName}_transcript"
if ($LogTranscript) {
Start-Transcript -Path $transcriptFile
}
Set-StrictMode -Version 1.0
Set-Variable -Name ProgressPreference -Value 'SilentlyContinue' # Default is "Continue"
Set-Variable -Name ErrorActionPreference -Value 'SilentlyContinue' # Default is "Continue"
$global:CreateTaskbarLayout = $true
$global:CreateDesktopShortcut = $true
$global:ExecuteInstallScripts = $false
try {
# Set current working directory to user's home directory
Set-Location $downloadFolder
# Ensure required folders exist
New-Item -Path $gistsFolder -ItemType Directory | Out-Null
$sources = @('extras')
$pkgs = @()
# Check if scoop is installed:
try {
$null = Get-Command -Name scoop -ErrorAction Stop
Write-Host '[Info] scoop.exe is already installed.' -ForegroundColor Blue
}
catch {
Write-Host '[Info] scoop.exe is not installed. Installing...' -ForegroundColor Blue
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression
# Configure scoop
Write-Host '[Scoop] Configuring scoop' -ForegroundColor DarkGray
scoop config aria2-warning-enabled false *> $null
scoop config cache_path "$scoopFolder" *> $null
# Use lessmsi
Write-Host '[Scoop] Using lessmsi' -ForegroundColor DarkGray
scoop config use_lessmsi `$true *> $null
# Install scoop essentials
Write-Host '[Scoop] Installing scoop essentials' -ForegroundColor DarkGray
scoop install lessmsi 7zip aria2 git *> $null
Write-Host '[Info] Installing Scoop apps registry modifications' -ForegroundColor Blue
reg import "${env:UserProfile}\scoop\apps\7zip\current\install-context.reg" *> $null
}
try {
# Add sources and install packages
$sources | ForEach-Object {
Write-Host "[Scoop] Adding scoop bucket '$_'..." -ForegroundColor DarkGray
scoop bucket add $_ *> $null
}
$pkgs | ForEach-Object {
Write-Host "[Scoop] Installing scoop package '$_'..." -ForegroundColor DarkGray
scoop install $_ *> $null
}
}
catch {
Write-Host "[Scoop:Error] $_" -ForegroundColor Red
}
# Download gists from GitHub
try {
Write-Host '[Gist] Downloading gists from GitHub...' -ForegroundColor DarkGray
if (-not (Test-Path "$gistsFolder\gist_urls")) {
Invoke-RestMethod -Uri "https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---gist_urls" | Out-File -FilePath "$gistsFolder\gist_urls" -Force
}
$arguments = @("--dir=$gistsFolder", "--input-file=$gistsFolder\gist_urls", '--quiet', '--allow-overwrite=false', '--auto-file-renaming=false', '--file-allocation=none')
Start-Process -FilePath 'aria2c' -NoNewWindow -Wait -ArgumentList $arguments | Out-Null
}
catch {
Write-Host "[Gist:Error] $_" -ForegroundColor Red
}
# Get first image from folder and set as wallpaper
$wallpaper = Get-ChildItem -Path "$sandboxFolder\*" -Include *.jpg, *.png | Sort-Object LastWriteTime -Descending | Select-Object -First 1
if ($wallpaper) {
Write-Host "[Info] Setting wallpaper to '$($wallpaper.Name)'..." -ForegroundColor Blue
UpdateWallpaper("$($wallpaper.FullName)")
}
else {
Write-Host "[Warning] No wallpaper found in '$sandboxFolder', downloading random wallpaper from Unsplash..." -ForegroundColor Yellow
$url = 'https://source.unsplash.com/random/1920x1080/?wallpaper,dark,abstract,bokeh'
$wallpaper = "$sandboxFolder\wallpaper.jpg"
[Utils]::DownloadFile($url, $sandboxFolder, 'wallpaper.jpg')
UpdateWallpaper($wallpaper)
}
# Performing Windows Registry hacks
Write-Host '[Info] Configuring Windows registry...' -ForegroundColor Blue
if ( -Not [Registry]::ConfigureWindows() ) {
Write-Host '[Error] Failed to configure Windows registry...' -ForegroundColor Red
}
# Create desktop shortcut to scripts folder (enabled)
if ($global:CreateDesktopShortcut) {
Write-Host "[Info] Creating desktop shortcut 'Home' for '${env:UserProfile}'" -ForegroundColor Magenta
[Utils]::NewShortcut("${env:UserProfile}", "${env:UserProfile}\Desktop\Home.lnk")
}
# Create desktop shortcut to scripts folder (enabled)
if ($global:CreateDesktopShortcut) {
Write-Host "[Info] Creating desktop shortcut 'Install scripts' for '$scriptsFolder\installers'" -ForegroundColor Magenta
[Utils]::NewShortcut("$installersFolder", "${env:UserProfile}\Desktop\Install scripts.lnk")
}
# Create appropriate taskbar layout (enabled)
if ($global:CreateTaskbarLayout) {
# <?xml version="1.0" encoding="utf-8"?>
# <LayoutModificationTemplate
# xmlns="http://schemas.microsoft.com/Start/2014/LayoutModification"
# xmlns:defaultlayout="http://schemas.microsoft.com/Start/2014/FullDefaultLayout"
# xmlns:start="http://schemas.microsoft.com/Start/2014/StartLayout"
# xmlns:taskbar="http://schemas.microsoft.com/Start/2014/TaskbarLayout"
# Version="1">
# <CustomTaskbarLayoutCollection PinListPlacement="Replace">
# <defaultlayout:TaskbarLayout>
# <taskbar:TaskbarPinList>
# <taskbar:DesktopApp DesktopApplicationID="Microsoft.Windows.Explorer" />
# <taskbar:DesktopApp DesktopApplicationLinkPath="%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\Microsoft Edge.lnk" />
# </taskbar:TaskbarPinList>
# </defaultlayout:TaskbarLayout>
# </CustomTaskbarLayoutCollection>
# </LayoutModificationTemplate>
Write-Host '[Info] Creating taskbar layout' -ForegroundColor Blue
$taskbarLayoutModificationFileData = 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjxMYXlvdXRNb2RpZmljYXRpb25UZW1wbGF0ZQ0KICAgIHhtbG5zPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL1N0YXJ0LzIwMTQvTGF5b3V0TW9kaWZpY2F0aW9uIg0KICAgIHhtbG5zOmRlZmF1bHRsYXlvdXQ9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vU3RhcnQvMjAxNC9GdWxsRGVmYXVsdExheW91dCINCiAgICB4bWxuczpzdGFydD0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS9TdGFydC8yMDE0L1N0YXJ0TGF5b3V0Ig0KICAgIHhtbG5zOnRhc2tiYXI9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vU3RhcnQvMjAxNC9UYXNrYmFyTGF5b3V0Ig0KICAgIFZlcnNpb249IjEiPg0KICA8Q3VzdG9tVGFza2JhckxheW91dENvbGxlY3Rpb24gUGluTGlzdFBsYWNlbWVudD0iUmVwbGFjZSI+DQogICAgPGRlZmF1bHRsYXlvdXQ6VGFza2JhckxheW91dD4NCiAgICAgIDx0YXNrYmFyOlRhc2tiYXJQaW5MaXN0Pg0KICAgICAgICA8dGFza2JhcjpEZXNrdG9wQXBwIERlc2t0b3BBcHBsaWNhdGlvbklEPSJNaWNyb3NvZnQuV2luZG93cy5FeHBsb3JlciIgLz4NCiAgICAgICAgPHRhc2tiYXI6RGVza3RvcEFwcCBEZXNrdG9wQXBwbGljYXRpb25MaW5rUGF0aD0iJVBST0dSQU1EQVRBJVxNaWNyb3NvZnRcV2luZG93c1xTdGFydCBNZW51XFByb2dyYW1zXE1pY3Jvc29mdCBFZGdlLmxuayIgLz4NCiAgICAgIDwvdGFza2JhcjpUYXNrYmFyUGluTGlzdD4NCiAgICA8L2RlZmF1bHRsYXlvdXQ6VGFza2JhckxheW91dD4NCiA8L0N1c3RvbVRhc2tiYXJMYXlvdXRDb2xsZWN0aW9uPg0KPC9MYXlvdXRNb2RpZmljYXRpb25UZW1wbGF0ZT4NCg=='
[Utils]::SetTaskbarLayout($dataFolder, $taskbarLayoutModificationFileData)
}
[Utils]::RestartExplorer()
}
catch {
Write-Host "[Error] $_" -ForegroundColor Red
}
finally {
Stop-Transcript
}
class Taskbar {
static [hashtable[]] $registry = @(
)
static [hashtable[]] $pinnedItems = @(
)
static [void] RestorePinnedTaskbarIcons() {
<#
.SYNOPSIS
Restores pinned taskbar icons.
.DESCRIPTION
This function restores the pinned taskbar icons by removing the relevant registry keys and shortcut files.
.PARAMETER None
This function does not accept any parameters.
.EXAMPLE
RestorePinnedTaskbarIcons
This example demonstrates how to use the RestorePinnedTaskbarIcons function to restore the pinned taskbar icons.
#>
Write-Host '[Info] Restoring pinned taskbar icons...' -ForegroundColor Blue
Remove-Item -Path 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Taskband' -Recurse -ErrorAction SilentlyContinue
Remove-Item "$env:AppData\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\*.lnk" -ErrorAction SilentlyContinue
try {
[Taskbar]::RestoreFiles()
[Utils]::RestoreRegistry([Taskbar]::registry)
}
catch {
throw $_.Exception.Message
}
}
static [void] RestoreFiles() {
foreach ($pinnedItem in [Taskbar]::pinnedItems) {
$filePath = [Utils]::WriteFile($pinnedItem.data, $pinnedItem.path)
Write-Host "[OK] Created '$($pinnedItem.name)' link" -ForegroundColor Green
}
}
}
class DesktopIcons {
static [hashtable[]] $registry = @(
@{path = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel'; displayName = 'My Computer'; name = '{20D04FE0-3AEA-1069-A2D8-08002B30309D}'; propertyType = 'DWord'; data = 0 }
@{path = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel'; displayName = 'Recycle bin'; name = '{645FF040-5081-101B-9F08-00AA002F954E}'; propertyType = 'DWord'; data = 1 }
@{path = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel'; displayName = "User's Files"; name = '{59031A47-3F72-44A7-89C5-5595FE6B30EE}'; propertyType = 'DWord'; data = 1 }
@{path = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel'; displayName = 'OneDrive'; name = '{018D5C66-4533-4307-9B53-224DE2ED1FE6}'; propertyType = 'DWord'; data = 1 }
)
static [void] RestoreDesktopIcons() {
<#
.SYNOPSIS
Restores desktop icons by removing all shortcut files from the Public and User desktops and restoring the registry settings.
.DESCRIPTION
This function removes all shortcut files from the Public and User desktops and then calls the RestoreRegistry method of the Utils class to restore the registry settings for desktop icons.
.PARAMETER None
This function does not accept any parameters.
.EXAMPLE
RestoreDesktopIcons
This example demonstrates how to use the RestoreDesktopIcons function to restore desktop icons.
#>
Write-Host '[Info] Removing desktop icons...' -ForegroundColor Blue
Remove-Item "$env:Public\Desktop\*.lnk" -ErrorAction SilentlyContinue
Remove-Item "$env:UserProfile\Desktop\*.lnk" -ErrorAction SilentlyContinue
[Utils]::RestoreRegistry([DesktopIcons]::registry)
}
}
class OtherRegistrySettings {
static [hashtable[]] $registry = @(
@{path = 'HKCU:\Control Panel\International\Geo'; name = 'Nation'; propertyType = 'String'; data = '176' }
@{path = 'HKCU:\Control Panel\International\Geo'; name = 'Name'; propertyType = 'String'; data = 'NL' }
@{path = 'HKCU:\Control Panel\International'; name = 'Locale'; propertyType = 'String'; data = '00000413' }
@{path = 'HKCU:\Control Panel\International'; name = 'LocaleName'; propertyType = 'String'; data = 'nl-NL' }
@{path = 'HKCU:\Control Panel\International'; name = 's1159'; propertyType = 'String'; data = '' }
@{path = 'HKCU:\Control Panel\International'; name = 's2359'; propertyType = 'String'; data = '' }
@{path = 'HKCU:\Control Panel\International'; name = 'sCurrency'; propertyType = 'String'; data = '€' }
@{path = 'HKCU:\Control Panel\International'; name = 'sDate'; propertyType = 'String'; data = '-' }
@{path = 'HKCU:\Control Panel\International'; name = 'sDecimal'; propertyType = 'String'; data = ',' }
@{path = 'HKCU:\Control Panel\International'; name = 'sLanguage'; propertyType = 'String'; data = 'NLD' }
@{path = 'HKCU:\Control Panel\International'; name = 'sList'; propertyType = 'String'; data = ';' }
@{path = 'HKCU:\Control Panel\International'; name = 'sLongDate'; propertyType = 'String'; data = 'dddd d MMMM yyyy' }
@{path = 'HKCU:\Control Panel\International'; name = 'sMonDecimalSep'; propertyType = 'String'; data = ',' }
@{path = 'HKCU:\Control Panel\International'; name = 'sMonThousandSep'; propertyType = 'String'; data = '.' }
@{path = 'HKCU:\Control Panel\International'; name = 'sShortDate'; propertyType = 'String'; data = 'd-M-yyyy' }
@{path = 'HKCU:\Control Panel\International'; name = 'sThousand'; propertyType = 'String'; data = '.' }
@{path = 'HKCU:\Control Panel\International'; name = 'iCountry'; propertyType = 'String'; data = '31' }
@{path = 'HKCU:\Control Panel\International'; name = 'iCurrency'; propertyType = 'String'; data = '2' }
@{path = 'HKCU:\Control Panel\International'; name = 'iFirstWeekOfYear'; propertyType = 'String'; data = '2' }
@{path = 'HKCU:\Control Panel\International'; name = 'iNegCurr'; propertyType = 'String'; data = '12' }
@{path = 'HKCU:\Control Panel\International'; name = 'sShortTime'; propertyType = 'String'; data = 'HH:mm' }
@{path = 'HKCU:\Control Panel\International'; name = 'sTimeFormat'; propertyType = 'String'; data = 'HH:mm:ss' }
@{path = 'HKCU:\Control Panel\Desktop'; name = 'PaintDesktopVersion'; propertyType = 'DWord'; data = 1 }
@{path = 'HKCU:\Control Panel\Desktop'; name = 'DragFullWindows'; propertyType = 'DWord'; data = 0 }
@{path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer'; name = 'NoRecycleFiles'; propertyType = 'DWord'; data = 1 }
@{path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced'; name = 'LaunchTo'; propertyType = 'DWord'; data = 1 }
@{path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced'; name = 'NavPaneShowAllFolders'; propertyType = 'DWord'; data = 0 }
@{path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced'; name = 'HideFileExt'; propertyType = 'DWord'; data = 0 }
@{path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced'; name = 'ListviewAlphaSelect'; propertyType = 'DWord'; data = 0 }
@{path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced'; name = 'ListviewShadow'; propertyType = 'DWord'; data = 0 }
@{path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced'; name = 'TaskbarAnimations'; propertyType = 'DWord'; data = 0 }
@{path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced'; name = 'ShowTaskViewButton'; propertyType = 'DWord'; data = 0 }
@{path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VisualEffects'; name = 'VisualFXSetting'; propertyType = 'DWord'; data = 3 }
@{path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize'; name = 'AppsUseLightTheme'; propertyType = 'DWord'; data = 0 }
@{path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize'; name = 'SystemUsesLightTheme'; propertyType = 'DWord'; data = 0 }
@{path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize'; name = 'ColorPrevalence'; propertyType = 'DWord'; data = 0 }
@{path = 'HKCU:\SOFTWARE\Microsoft\Clipboard'; name = 'EnableClipboardHistory'; propertyType = 'DWord'; data = 1 }
@{path = 'HKCU:\SOFTWARE\Microsoft\Windows\DWM'; name = 'EnableAeroPeek'; propertyType = 'DWord'; data = 0 }
)
}
class Registry {
static [hashtable[]] $list = @(
@{name = 'HideTaskbarSearch' }
@{name = 'DisableTelemetry' }
@{name = 'DisableEdgeHubSidebar' }
@{name = 'DisableWebSearch' }
@{name = 'DisableMapUpdates' }
@{name = 'HideChatIcon' }
@{name = 'DisableWindowsSpotlightFeatures' }
@{name = 'DisableWindowsWidgets' }
@{name = 'SetLongDateFormat' }
@{name = 'SetShortDateFormat' }
)
static [bool] ConfigureWindows() {
try {
foreach ($method in [Registry]::list) {
try {
$expr = "[Registry]::$($method.name)()"
Invoke-Expression $expr
}
catch {
Write-Host "[Error] Unable to invoke static method [Registry]::$($method.name)()" -ForegroundColor Red
}
}
Write-Host '[Info] Restore pinned taskbar icons...' -ForegroundColor Blue
[Taskbar]::RestorePinnedTaskbarIcons()
Write-Host '[Info] Restore desktop icons...' -ForegroundColor Blue
[DesktopIcons]::RestoreDesktopIcons()
Write-Host '[Info] Setting other registry values...' -ForegroundColor Blue
[Utils]::RestoreRegistry([OtherRegistrySettings]::registry)
return $true
}
catch {
return $false
}
}
static [void] HideTaskbarSearch() {
Write-Host '[Info] Hiding Taskbar Search box / button...' -ForegroundColor Blue
Set-ItemProperty -Path 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Search' -Name 'SearchboxTaskbarMode' -Type DWord -Value 0
}
static [void] ShowTaskbarSearchIcon() {
Write-Host '[Info] Showing Taskbar Search icon...' -ForegroundColor Blue
Set-ItemProperty -Path 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Search' -Name 'SearchboxTaskbarMode' -Type DWord -Value 1
}
static [void] ShowTaskbarSearchBox() {
Write-Host '[Info] Showing Taskbar Search box...' -ForegroundColor Blue
Set-ItemProperty -Path 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Search' -Name 'SearchboxTaskbarMode' -Type DWord -Value 2
}
# Disable Telemetry
static [void] DisableTelemetry() {
Write-Host '[Info] Disabling Telemetry...' -ForegroundColor Blue
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection' -Name 'AllowTelemetry' -Type DWord -Value 0
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection' -Name 'AllowTelemetry' -Type DWord -Value 0
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Policies\DataCollection' -Name 'AllowTelemetry' -Type DWord -Value 0
Get-ScheduledTask -TaskPath '\Microsoft\Windows\Application Experience\' -TaskName 'Microsoft Compatibility Appraiser' -ErrorAction SilentlyContinue | Disable-ScheduledTask | Out-Null
Get-ScheduledTask -TaskPath '\Microsoft\Windows\Application Experience\' -TaskName 'ProgramDataUpdater' -ErrorAction SilentlyContinue | Disable-ScheduledTask | Out-Null
Get-ScheduledTask -TaskPath '\Microsoft\Windows\Autochk\' -TaskName 'Proxy' -ErrorAction SilentlyContinue | Disable-ScheduledTask | Out-Null
Get-ScheduledTask -TaskPath '\Microsoft\Windows\Customer Experience Improvement Program\' -TaskName 'Consolidator' -ErrorAction SilentlyContinue | Disable-ScheduledTask | Out-Null
Get-ScheduledTask -TaskPath '\Microsoft\Windows\Customer Experience Improvement Program\' -TaskName 'UsbCeip' -ErrorAction SilentlyContinue | Disable-ScheduledTask | Out-Null
Get-ScheduledTask -TaskPath '\Microsoft\Windows\DiskDiagnostic\' -TaskName 'Microsoft-Windows-DiskDiagnosticDataCollector' -ErrorAction SilentlyContinue | Disable-ScheduledTask | Out-Null
}
# Enable Telemetry
static [void] EnableTelemetry() {
Write-Host '[Info] Enabling Telemetry...' -ForegroundColor Blue
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection' -Name 'AllowTelemetry' -Type DWord -Value 3
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection' -Name 'AllowTelemetry' -Type DWord -Value 3
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Policies\DataCollection' -Name 'AllowTelemetry' -Type DWord -Value 3
Get-ScheduledTask -TaskPath '\Microsoft\Windows\Application Experience\' -TaskName 'Microsoft Compatibility Appraiser' -ErrorAction SilentlyContinue | Enable-ScheduledTask | Out-Null
Get-ScheduledTask -TaskPath '\Microsoft\Windows\Application Experience\' -TaskName 'ProgramDataUpdater' -ErrorAction SilentlyContinue | Enable-ScheduledTask | Out-Null
Get-ScheduledTask -TaskPath '\Microsoft\Windows\Autochk\' -TaskName 'Proxy' -ErrorAction SilentlyContinue | Enable-ScheduledTask | Out-Null
Get-ScheduledTask -TaskPath '\Microsoft\Windows\Customer Experience Improvement Program\' -TaskName 'Consolidator' -ErrorAction SilentlyContinue | Enable-ScheduledTask | Out-Null
Get-ScheduledTask -TaskPath '\Microsoft\Windows\Customer Experience Improvement Program\' -TaskName 'UsbCeip' -ErrorAction SilentlyContinue | Enable-ScheduledTask | Out-Null
Get-ScheduledTask -TaskPath '\Microsoft\Windows\DiskDiagnostic\' -TaskName 'Microsoft-Windows-DiskDiagnosticDataCollector' -ErrorAction SilentlyContinue | Enable-ScheduledTask | Out-Null
}
# Disable Web Search in Start Menu
static [void] DisableWebSearch() {
Write-Host '[Info] Disabling Bing Search in Start Menu...' -ForegroundColor Blue
Set-ItemProperty -Path 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Search' -Name 'BingSearchEnabled' -Type DWord -Value 0
If (!(Test-Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search')) {
New-Item -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search' -Force | Out-Null
}
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search' -Name 'DisableWebSearch' -Type DWord -Value 1
}
# Enable Web Search in Start Menu
static [void] EnableWebSearch() {
Write-Host '[Info] Enabling Bing Search in Start Menu...' -ForegroundColor Blue
Remove-ItemProperty -Path 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Search' -Name 'BingSearchEnabled' -ErrorAction SilentlyContinue
Remove-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search' -Name 'DisableWebSearch' -ErrorAction SilentlyContinue
}
# Disable Edge HubSidebar
static [void] DisableEdgeHubSidebar() {
Write-Host '[Info] Disabling Edge Hub Sidebar...' -ForegroundColor Blue
If (!(Test-Path 'HKLM:\SOFTWARE\Policies\Microsoft\Edge')) {
New-Item -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Edge' -Force | Out-Null
}
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Edge' -Name 'HubsSidebarEnabled' -Type DWord -Value 0
}
# Enable Edge HubSidebar
static [void] EnableEdgeHubSidebar() {
Write-Host '[Info] Enabling Edge Hub Sidebar...' -ForegroundColor Blue
If (!(Test-Path 'HKLM:\SOFTWARE\Policies\Microsoft\Edge')) {
New-Item -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Edge' -Force | Out-Null
}
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Edge' -Name 'HubsSidebarEnabled' -Type DWord -Value 1
}
# Disable Windows 11 widgets
static [void] DisableWindowsWidgets() {
Write-Host '[Info] Disabling Windows 11 widgets...' -ForegroundColor Blue
Set-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced' -Name 'TaskbarDa' -Type DWord -Value 0
}
# Enable Windows 11 widgets
static [void] EnableWindowsWidgets() {
Write-Host '[Info] Enabling Windows 11 widgets...' -ForegroundColor Blue
Set-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced' -Name 'TaskbarDa' -Type DWord -Value 1
}
# Disable automatic Maps updates
static [void] DisableMapUpdates() {
Write-Host '[Info] Disabling automatic Maps updates...' -ForegroundColor Blue
Set-ItemProperty -Path 'HKLM:\SYSTEM\Maps' -Name 'AutoUpdateEnabled' -Type DWord -Value 0
}
# Enable automatic Maps updates
static [void] EnableMapUpdates() {
Write-Host '[Info] Enabling automatic Maps updates...' -ForegroundColor Blue
Remove-ItemProperty -Path 'HKLM:\SYSTEM\Maps' -Name 'AutoUpdateEnabled' -ErrorAction SilentlyContinue
}
# Hide chat icon
static [void] HideChatIcon() {
Write-Host '[Info] Hiding taskbar Chat icon...' -ForegroundColor Blue
Set-ItemProperty -Path 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced' -Name 'TaskbarMn' -Type DWord -Value 0
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows' -Name 'ChatIcon' -Type DWord -Value 0x03
}
# Show chat icon
static [void] ShowChatIcon() {
Write-Host '[Info] Showing taskbar Chat icon...' -ForegroundColor Blue
Set-ItemProperty -Path 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced' -Name 'TaskbarMn' -Type DWord -Value 1
Remove-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows' -Name 'ChatIcon' -ErrorAction SilentlyContinue
}
# Disable Windows Desktop Spotlight (DisableWindowsSpotlightFeatures)
static [void] DisableWindowsSpotlightFeatures() {
Write-Host "[Info] Disabling Windows Desktop Spotlight..." -ForegroundColor Blue
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CloudContent" -Name "DisableWindowsSpotlightFeatures" -Type DWord -Value 1
}
# Enable Windows Desktop Spotlight (DisableWindowsSpotlightFeatures)
static [void] EnableWindowsSpotlightFeatures() {
Write-Host "[Info] Enabling Windows Desktop Spotlight..." -ForegroundColor Blue
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CloudContent" -Name "DisableWindowsSpotlightFeatures" -Type DWord -Value 0
}
# Enable 'show file extensions' option
static [void] ShowFileExtensions() {
Write-Host "[Info] Enabling 'show file extensions' option..." -ForegroundColor Blue
Set-ItemProperty -Path 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced' -Name 'HideFileExt' -Type DWord -Value 0
}
# Disable 'show file extensions' option
static [void] HideFileExtensions() {
Write-Host "[Info] Disabling 'show file extensions' option..." -ForegroundColor Blue
Set-ItemProperty -Path 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced' -Name 'HideFileExt' -Type DWord -Value 1
}
static [void] SetShortTimeFormat() {
Write-Host '[Info] Setting short time format (HH:mm:ss)...' -ForegroundColor Blue
Set-ItemProperty -Path 'HKCU:\Control Panel\International' -Name sShortTime -Value 'hh:mm:ss'
}
static [void] SetLongTimeFormat() {
Write-Host '[Info] Setting long time format (HH:mm:ss)...' -ForegroundColor Blue
Set-ItemProperty -Path 'HKCU:\Control Panel\International' -Name sTimeFormat -Value 'hh:mm:ss'
}
static [void] SetLongDateFormat() {
Write-Host '[Info] Setting long date format (dddd, MMMM d, yyyy)...' -ForegroundColor Blue
Set-ItemProperty -Path 'HKCU:\Control Panel\International' -Name sLongDate -Value 'dddd, MMMM d, yyyy'
}
static [void] SetShortDateFormat() {
Write-Host '[Info] Setting short date format (d-M-yyyy)...' -ForegroundColor Blue
Set-ItemProperty -Path 'HKCU:\Control Panel\International' -Name sShortDate -Value 'd-M-yyyy'
}
}
# Title: Utils class
# Author: B. van Wetten
# Last Update: 2024-01-03
class Utils {
# Static variable to control debug mode
static [bool] $DebugMode = $false
# Method to output a debug message
static [void] OutputDebugMessage([string] $message) {
if ([Utils]::DebugMode) {
Write-Host "[Debug] $message" -ForegroundColor DarkGray
}
}
# Method to enable or disable debug mode
static [void] SetDebugMode([bool] $mode) {
[Utils]::DebugMode = $mode
}
static [void] CreateRegistryPath([string] $KeyPath) {
<#
.SYNOPSIS
Recursively creates a new registry path, creating any intermediate subkeys that do not exist.
.DESCRIPTION
This method works similarly to 'mkdir -p' for directories. It creates the specified registry path, including all intermediate subkeys that do not already exist.
.PARAMETER KeyPath
The full path of the registry key to create.
.EXAMPLE
Utils::CreateRegistryPath -KeyPath "HKCU:\Software\MyApp\Settings"
This example creates the registry path "HKCU:\Software\MyApp\Settings", including any intermediate subkeys that do not already exist.
#>
try {
# Split the KeyPath into individual keys
$keys = $KeyPath -split '\\'
$currentPath = ''
foreach ($key in $keys) {
# Build the current path incrementally
$currentPath = if ($currentPath) { $currentPath + '\' + $key } else { $key }
# Check if the current path exists, if not, create it
if (-not (Test-Path $currentPath)) {
New-Item -Path $currentPath -Force > $null
}
}
}
catch {
Write-Host "[Error] Failed to create registry path at $KeyPath. Error: $_" -ForegroundColor Red
}
}
static [void] SetRegistryValue([string] $KeyPath, [string] $Name, [string] $Value) { [Utils]::SetRegistryValue($KeyPath, $Name, $Value, "String") }
static [void] SetRegistryValue(
[string]$KeyPath,
[string]$Name,
[string]$Value,
[string]$Type = 'String'
) {
<#
.SYNOPSIS
Sets a registry value at the specified key path with the given name, value, and type.
.DESCRIPTION
This function sets a registry value at the specified key path with the given name, value, and type. It performs manual validation for the specified type and creates the registry key if it doesn't exist.
.PARAMETER KeyPath
The path of the registry key where the value will be set.
.PARAMETER Name
The name of the registry value.
.PARAMETER Value
The value to be set.
.PARAMETER Type
The type of the registry value. Valid options are: String, ExpandString, Binary, DWord, MultiString, QWord, None. Default is String.
.EXAMPLE
SetRegistryValue -KeyPath "HKCU:\Software\MyApp" -Name "Version" -Value "1.0" -Type String
This example sets a registry value named "Version" with the value "1.0" at the key path "HKCU:\Software\MyApp" using the default type (String).
#>
try {
# Manual validation for $Type
$validTypes = @('String', 'ExpandString', 'Binary', 'DWord', 'MultiString', 'QWord', 'None')
if ($Type -notin $validTypes) {
throw "Invalid Type specified. Valid options are: $($validTypes -join ', ')"
}
if (-not (Test-Path "$KeyPath")) {
[Utils]::CreateRegistryPath("$KeyPath")
}
Set-ItemProperty -Path "$KeyPath" -Name $Name -Value $Value -Type $Type
}
catch {
Write-Host "[Error] Failed to set registry value at path $KeyPath with name $Name. Error: $_" -ForegroundColor Red
}
}
static [void] DownloadFile([string]$fileUri, [string]$outputFolder) { [Utils]::DownloadFile($fileUri, $outputFolder, "") }
static [void] DownloadFile([string]$fileUri, [string]$outputFolder, [string] $fileName = "") {
<#
.SYNOPSIS
Downloads a file from a specified URL to a specified output folder.
.DESCRIPTION
The DownloadFile function downloads a file from the specified URL to the specified output folder. If the file already exists in the output folder, it displays an information message. If the download is successful, it displays a success message. If the download fails, it displays an error message.
.PARAMETER fileUri
The URL of the file to be downloaded.
.PARAMETER outputFolder
The path of the folder where the downloaded file will be saved.
.EXAMPLE
DownloadFile -fileUri "https://example.com/file.txt" -outputFolder "C:\Downloads"
Downloads the file from the specified URL and saves it to the specified output folder.
#>
if ($fileName -eq "") {
$fileName = Split-Path -Path $fileUri -Leaf
}
$outputPath = Join-Path -Path $outputFolder -ChildPath $fileName
$outputPath = Join-Path -Path $outputFolder -ChildPath $fileName
if (Test-Path $outputPath) {
Write-Host "[Info] $fileName already downloaded..." -ForegroundColor Magenta
}
else {
# Test if the folder exists, otherwise create it
if (-not (Test-Path $outputFolder)) {
New-Item -Path $outputFolder -ItemType Directory | Out-Null
}
$response = Invoke-WebRequest -Passthru -Uri $fileUri -OutFile $outputPath
if ($response.StatusCode -eq 200 -and (Test-Path $outputPath)) {
Write-Host "[OK] $fileName downloaded successfully..." -ForegroundColor Green
}
else {
Write-Host "[Error] Failed to download $fileName..." -ForegroundColor Red
}
}
}
static [void] WingetInstall([string] $packageId, [string] $wingetFolder) {
<#
.SYNOPSIS
Installs a package using winget with local caching.
.DESCRIPTION
This method installs a package using winget. If the package is already downloaded in the specified folder, it installs from there, otherwise downloads it.
.PARAMETER packageId
The ID of the package to install.
.PARAMETER wingetFolder
The folder to check for cached package installers.
.EXAMPLE
[Utils]::WingetInstall "Microsoft.VisualStudioCode" "C:\winget-cache"
Installs Visual Studio Code, using a local cache if available.
#>
Write-Host "[Info] Installing $packageId..." -ForegroundColor Blue
$packageDetails = winget.exe show -e --accept-source-agreements --id $packageId
$matches = [regex]::Matches($packageDetails, "Installer\sUrl:\s.*\/([^\/]+)(\.(?:exe|msi|msix))")
# Debug output
[Utils]::OutputDebugMessage("[Debug] packageId: $packageId")
[Utils]::OutputDebugMessage("[Debug] packageDetails: $packageDetails")
[Utils]::OutputDebugMessage("[Debug] matches: $matches")
if ($matches.Count -eq 1) {
$filename = $matches[0].Groups[1].Value
$filenameExt = $matches[0].Groups[2].Value
winget.exe install -h --exact --accept-source-agreements --accept-package-agreements --id $packageId
# # Debug output
# [Utils]::OutputDebugMessage("[Debug] filename: $filename")
# [Utils]::OutputDebugMessage("[Debug] filenameExt: $filenameExt")
# # Create the folder if it doesn't exist
# if (-not (Test-Path "$wingetFolder\$packageId")) {
# New-Item -Path "$wingetFolder\$packageId" -ItemType Directory | Out-Null
# Write-Host "[Info] Downloading $packageId..." -ForegroundColor Blue
# winget.exe download --exact --scope machine --accept-source-agreements --accept-package-agreements --id $packageId --download-directory "$wingetFolder\$packageId"
# }
# $manifestFile = $wingetFolder + "\" + $packageId + "\$packageId.yaml"
# $installerFile = $wingetFolder + "\" + $packageId + "\$packageId$filenameExt"
# # Debug output
# [Utils]::OutputDebugMessage("[Debug] manifestFile: $manifestFile")
# [Utils]::OutputDebugMessage("[Debug] installerFile: $installerFile")
# # Check if both files exist, otherwise throw an error
# if (-not (Test-Path $manifestFile) -or -not (Test-Path $installerFile)) {
# throw "Unable to find installer for package $packageId"
# }
# # Debug output
# [Utils]::OutputDebugMessage("[Debug] winget.exe install -h --exact --accept-source-agreements --accept-package-agreements --id $packageId '$installerFile'")
# # Install the package
# winget.exe install -h --exact --accept-source-agreements --accept-package-agreements --id $packageId "$installerFile"
}
else {
throw "Unable to find installer URL for package $packageId"
}
}
static [void] NewShortcut([String] $Target, [String] $Destination) {
<#
.SYNOPSIS
Creates a new shortcut.
.DESCRIPTION
This function creates a new shortcut using the Windows Scripting Shell.
.PARAMETER Target
The target path of the shortcut.
.PARAMETER Destination
The destination path where the shortcut will be saved.
.EXAMPLE
NewShortcut -Target "C:\Program Files\MyApp\MyApp.exe" -Destination "C:\Users\Username\Desktop\MyApp.lnk"
Creates a new shortcut for the application "MyApp" on the user's desktop.
.LINK
https://stackoverflow.com/a/9701907/16036749
#>
$WScriptShell = New-Object -ComObject WScript.Shell
$Shortcut = $WScriptShell.CreateShortcut($Destination)
$Shortcut.TargetPath = $Target
$Shortcut.Save()
}
static [void] RestoreRegistry([hashtable[]] $registrySettings) {
<#
.SYNOPSIS
Restores registry settings based on the provided hashtable array.
.DESCRIPTION
This function restores registry settings by iterating through the provided hashtable array and creating or updating registry keys and values accordingly.
.PARAMETER registrySettings
An array of hashtables containing the registry settings to be restored. Each hashtable should have the following properties:
- path: The path of the registry key.
- name: The name of the registry value.
- propertyType: The type of the registry value.
- data: The data to be set for the registry value.
.EXAMPLE
$registrySettings = @(
@{
path = "HKCU:\Software\MyApp"
name = "Version"
propertyType = "String"
data = "1.0"
},
@{
path = "HKLM:\Software\MyApp"
name = "InstallDir"
propertyType = "ExpandString"
data = "C:\Program Files\MyApp"
}
)
RestoreRegistry -registrySettings $registrySettings
#>
foreach ($registry in $registrySettings) {
If (!(Test-Path $registry.path)) {
New-Item -Path $registry.path -Force | Out-Null
}
[Utils]::OutputDebugMessage("Attempting to create '$('{0}\{1}' -f $registry.path, $registry.name)'")
switch ($registry.propertyType) {
'Binary' { [Utils]::SetRegistryValue($registry.path, $registry.name, ([byte[]]([System.Convert]::FromHexString($registry.data))), $registry.propertyType) }
'DWord' { [Utils]::SetRegistryValue($registry.path, $registry.name, ([byte]($registry.data)), $registry.propertyType) }
Default { [Utils]::SetRegistryValue($registry.path, $registry.name, ($registry.data), $registry.propertyType) }
}
}
}
static [void] Pause ([string] $msg) { [Utils]::Pause($msg, $null) }
static [void] Pause ([string] $msg, [int] $timeout = 0) {
<#
.SYNOPSIS
Pauses the script execution and waits for user input or a timeout.
.DESCRIPTION
The Pause function pauses the script execution and waits for user input or a timeout to occur. It displays a message in yellow color before waiting for input.
.PARAMETER msg
The message to display before waiting for input.
.PARAMETER timeout
The maximum time (in seconds) to wait for user input before timing out. If not specified, the function waits indefinitely.
.EXAMPLE
Pause "Press any key to continue..." 5
Pauses the script execution and waits for 5 seconds for user input. If no input is received within the specified timeout, the function returns.
.EXAMPLE
Pause "Press any key to continue..."
Pauses the script execution and waits indefinitely for user input.
#>
try {
Write-Host "$msg" -ForegroundColor Yellow
$timer = New-Object System.Diagnostics.Stopwatch
$timer.Start()
while (-not [Console]::KeyAvailable -and ($timeout -eq 0 -or $timer.Elapsed.Seconds -lt $timeout)) {
Start-Sleep -Milliseconds 50
}
if ([Console]::KeyAvailable) {
[void][Console]::ReadKey($true)
}
}
catch {
throw $_.Exception.Message
}
}
static [bool] RemoveApp([string] $appName) {
<#
.SYNOPSIS
Removes an app from Windows 11.
.DESCRIPTION
This function removes an app from Windows 11 by uninstalling the app package and removing the provisioned package.
.PARAMETER appName
The name of the app to be removed.
.EXAMPLE
RemoveApp -appName "Microsoft.WindowsAlarms"
#>
try {
Get-AppxPackage $appName -AllUsers | Remove-AppxPackage
Get-AppXProvisionedPackage -Online | Where-Object DisplayName -like $appName | Remove-AppxProvisionedPackage -Online
return $true
}
catch {
throw $_.Exception.Message
}
}
static [void] RestartExplorer() {
<#
.SYNOPSIS
Restarts Windows Explorer.
.DESCRIPTION
This function restarts the Windows Explorer process. It first stops the existing Explorer process, waits for a few seconds, and then verifies if the process has restarted. If the process is not found, it attempts to start the Explorer process again. If any errors occur during the process, an exception is thrown.
.PARAMETER None
This function does not accept any parameters.
.EXAMPLE
RestartExplorer
Restarts the Windows Explorer process.
#>
Get-Process -Name Explorer | Stop-Process -Force
# Give Windows Explorer time to start
Start-Sleep -Seconds 3
# Verify that Windows Explorer has restarted
Try {
$p = Get-Process -Name Explorer -ErrorAction Stop
}
Catch {
Try {
Start-Process explorer.exe
}
Catch {
# This should never be called
Throw $_
}
}
}
static [void] EnsureDockerRunning() {
<#
.SYNOPSIS
Ensures that Docker is running on the Windows 11 Sandbox.
.DESCRIPTION
This function checks if Docker is already running on the Windows 11 Sandbox. If Docker is not running, it starts Docker Desktop assuming the default installation path. It then waits for Docker to start running or until a timeout is reached. If Docker does not start within the specified time, an error message is displayed and the script exits with a non-zero exit code.
.PARAMETER None
.INPUTS
None
.OUTPUTS
None
.EXAMPLE
EnsureDockerRunning
This example demonstrates how to use the EnsureDockerRunning function to ensure that Docker is running on the Windows 11 Sandbox.
#>
function IsDockerRunning {
try {
docker info > $null 2>&1
return $?
}
catch {
return $false
}
}
# Check if Docker is already running
if (IsDockerRunning) { exit 0 }
# Start Docker Desktop (assuming default installation path)
Start-Process "C:\Program Files\Docker\Docker\Docker Desktop.exe"
# Initialize variables
$timeoutMinutes = 1
$elapsedSeconds = 0
$isDockerRunning = $false
# Wait until Docker is running or timeout is reached
do {
# Check every 5 seconds
Start-Sleep -Seconds 5
$elapsedSeconds += 5
Write-Host "[Info] Waiting for Docker to start..." -ForegroundColor Blue
} while (-not (IsDockerRunning) -and $elapsedSeconds -lt ($timeoutMinutes * 60))
if (-not (IsDockerRunning)) {
Write-Host "[Error] Docker failed to start within $timeoutMinutes minutes. Exiting..." -ForegroundColor Red
exit 1
}
}
static [string] WriteFile([string] $encodedString) { return [Utils]::WriteFile($encodedString, ('{0}\{1}' -f $env:TEMP, [System.Guid]::NewGuid().ToString())) }
static [string] WriteFile([string] $encodedString, $file = ('{0}\{1}' -f $env:TEMP, [System.Guid]::NewGuid().ToString())) {
<#
.SYNOPSIS
Writes a file from a base64 encoded string.
.DESCRIPTION
This function takes a base64 encoded string and writes it to a file on the local system. The file path is generated using the current user's TEMP directory and a unique GUID.
.PARAMETER encodedString
The base64 encoded string representing the file content.
.PARAMETER path
The optional file path where the file will be written. If not provided, the file will be written to the current user's TEMP directory with a unique file name.
.EXAMPLE
$encodedString = "SGVsbG8gV29ybGQh"
$filePath = WriteFile -encodedString $encodedString
Write-Host "File written to: $filePath"
#>
try {
# Decode the base64 string
$decodedBytes = [System.Convert]::FromBase64String($encodedString)
# Ensure the directory exists
$directoryPath = [System.IO.Path]::GetDirectoryName($file)
if (-not (Test-Path $directoryPath)) {
New-Item -Path $directoryPath -ItemType Directory -Force | Out-Null
}
[System.IO.File]::WriteAllBytes($file, $decodedBytes)
return Get-Item -Path $file
}
catch {
Write-Host "[Error] Failed to write file from base64 string. Error: $_" -ForegroundColor Red
return $null
}
}
static [bool] WriteArchive([string] $archiveFilePath, [string] $extractPath) {
<#
.SYNOPSIS
Extracts a base64 encoded, gzipped, and tarred archive to a specified directory.
.DESCRIPTION
This function takes the path to a file containing a base64 encoded, gzipped, and tarred archive. It decodes the file, uncompresses it, and extracts the contents to the specified directory.
.PARAMETER archiveFilePath
The file path to the base64 encoded, gzipped, and tarred archive.
.PARAMETER extractPath
The directory path where the contents of the archive should be extracted.
.EXAMPLE
$archiveFilePath = "C:\path\to\encoded_archive.txt"
$extractPath = "C:\path\to\extract"
$result = [Utils]::WriteArchive($archiveFilePath, $extractPath)
if ($result) { Write-Host "Archive extracted successfully" }
else { Write-Host "Failed to extract archive" }
#>
try {
# Ensure that the archive file exists
if (-not (Test-Path $archiveFilePath)) {
Throw "Archive file not found at $archiveFilePath"
}
# Ensure the extract directory exists
if (-not (Test-Path $extractPath)) {
New-Item -Path $extractPath -ItemType Directory -Force | Out-Null
}
# Read the base64 encoded archive from file and decode it
$encodedString = Get-Content -Path $archiveFilePath
$decodedBytes = [System.Convert]::FromBase64String($encodedString)
# Write the decoded bytes to a temporary gzip file
$tempGzipPath = [System.IO.Path]::Combine($env:TEMP, [System.Guid]::NewGuid().ToString() + ".gz")
[System.IO.File]::WriteAllBytes($tempGzipPath, $decodedBytes)
# Use tar to extract the gzipped tarball
tar -xzf $tempGzipPath -C $extractPath
# Clean up the temporary gzip file
Remove-Item -Path $tempGzipPath
return $true
}
catch {
Write-Host "[Error] Failed to extract archive. Error: $_" -ForegroundColor Red
return $false
}
}
static SetTaskbarLayout([string] $filePath, [string] $encodedData) {
<#
.SYNOPSIS
Sets the taskbar layout by creating a 'TaskbarLayoutModification.xml' file and modifying the registry.
.DESCRIPTION
This function sets the taskbar layout by creating a 'TaskbarLayoutModification.xml' file and modifying the registry values related to the taskbar layout. It takes the file path and encoded data as input parameters.
.PARAMETER filePath
The path where the 'TaskbarLayoutModification.xml' file will be created.
.PARAMETER encodedData
The encoded data for the 'TaskbarLayoutModification.xml' file.
.EXAMPLE
SetTaskbarLayout -filePath "C:\Scripts" -encodedData "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjxMYXlvdXRNb2RpZmljYXRpb25UZW1wbGF0ZQ0KICAgIHhtbG5zPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL1N0YXJ0LzIwMTQvTGF5b3V0TW9kaWZpY2F0aW9uIg0KICAgIHhtbG5zOmRlZmF1bHRsYXlvdXQ9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vU3RhcnQvMjAxNC9GdWxsRGVmYXVsdExheW91dCINCiAgICB4bWxuczpzdGFydD0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS9TdGFydC8yMDE0L1N0YXJ0TGF5b3V0Ig0KICAgIHhtbG5zOnRhc2tiYXI9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vU3RhcnQvMjAxNC9UYXNrYmFyTGF5b3V0Ig0KICAgIFZlcnNpb249IjEiPg0KICA8Q3VzdG9tVGFza2JhckxheW91dENvbGxlY3Rpb24+DQogICAgPGRlZmF1bHRsYXlvdXQ6VGFza2JhckxheW91dD4NCiAgICAgIDx0YXNrYmFyOlRhc2tiYXJQaW5MaXN0Pg0KICAgICAgICA8dGFza2JhcjpEZXNrdG9wQXBwIERlc2t0b3BBcHBsaWNhdGlvbklEPSJNaWNyb3NvZnQuV2luZG93cy5FeHBsb3JlciIgLz4NCiAgICAgICAgPHRhc2tiYXI6RGVza3RvcEFwcCBEZXNrdG9wQXBwbGljYXRpb25MaW5rUGF0aD0iJUFQUERBVEElXE1pY3Jvc29mdFxXaW5kb3dzXFN0YXJ0IE1lbnVcUHJvZ3JhbXNcU3lzdGVtIFRvb2xzXENvbW1hbmQgUHJvbXB0LmxuayIgLz4NCiAgICAgIDwvdGFza2JhcjpUYXNrYmFyUGluTGlzdD4NCiAgICA8L2RlZmF1bHRsYXlvdXQ6VGFza2JhckxheW91dD4NCiA8L0N1c3RvbVRhc2tiYXJMYXlvdXRDb2xsZWN0aW9uPg0KPC9MYXlvdXRNb2RpZmljYXRpb25UZW1wbGF0ZQ=="
#>
try {
# Create 'TaskbarLayoutModification.xml' file
$taskbarLayoutModificationFile = "$filePath\TaskbarLayoutModification.xml"
if (!(Test-Path $filePath)) {
# Create directory
New-Item $filePath -ItemType Directory -Force | Out-Null
}
Write-Host "[Info] Creating 'TaskbarLayoutModification.xml' file" -ForegroundColor Blue
[Utils]::WriteFile($encodedData, $taskbarLayoutModificationFile) | Out-Null
[Utils]::SetRegistryValue("HKCU:\Software\Microsoft\Windows\CurrentVersion\Group Policy Objects\{E0CC53D9-2FFF-4D2E-BECB-333B41615D7E}User\Software\Policies\Microsoft\Windows\Explorer", "LockedStartLayout", 0x01, "DWord")
[Utils]::SetRegistryValue("HKCU:\Software\Microsoft\Windows\CurrentVersion\Group Policy Objects\{E0CC53D9-2FFF-4D2E-BECB-333B41615D7E}User\Software\Policies\Microsoft\Windows\Explorer", "ReapplyStartLayoutEveryLogon", 0x01, "DWord")
[Utils]::SetRegistryValue("HKCU:\Software\Microsoft\Windows\CurrentVersion\Group Policy Objects\{E0CC53D9-2FFF-4D2E-BECB-333B41615D7E}User\Software\Policies\Microsoft\Windows\Explorer", "StartLayoutFile", $taskbarLayoutModificationFile)
[Utils]::SetRegistryValue("HKCU:\Software\Policies\Microsoft\Windows\Explorer", "LockedStartLayout", 0x01, "DWord")
[Utils]::SetRegistryValue("HKCU:\Software\Policies\Microsoft\Windows\Explorer", "ReapplyStartLayoutEveryLogon", 0x01, "DWord")
[Utils]::SetRegistryValue("HKCU:\Software\Policies\Microsoft\Windows\Explorer", "StartLayoutFile", $taskbarLayoutModificationFile)
}
catch {
Write-Host "[Error] Failed to set taskbar layout. Error: $_" -ForegroundColor Red
}
}
<#
.SYNOPSIS
Adds an application to the TaskbarPinList in an XML file.
.DESCRIPTION
This function adds an application to the TaskbarPinList in an XML file. It takes the path of the XML file and the path of the application link as input parameters. The function loads the XML file, finds the TaskbarPinList node, creates a new DesktopApp element, appends it to the TaskbarPinList, and saves the updated XML back to the file.
.PARAMETER XmlFilePath
The path of the XML file containing the TaskbarPinList.
.PARAMETER AppLinkPath
The path of the application link to be added to the TaskbarPinList.
.EXAMPLE
AddAppToTaskbarPinList -XmlFilePath "C:\Layout.xml" -AppLinkPath "C:\Applications\MyApp.lnk"
Adds the application link "C:\Applications\MyApp.lnk" to the TaskbarPinList in the XML file "C:\Layout.xml".
.NOTES
This function requires the XML file to have the appropriate schema and namespace for the TaskbarPinList element.
#>
static [void] AddAppToTaskbarPinList([string] $XmlFilePath, [string] $AppLinkPath) {
try {
# Check if the XML file exists
if (-not (Test-Path $XmlFilePath)) {
throw "XML file not found at $XmlFilePath"
}
# Load the XML file
[xml]$xml = Get-Content -Path $XmlFilePath
# Define the namespace to handle the prefixed elements
$ns = New-Object System.Xml.XmlNamespaceManager($xml.NameTable)
$ns.AddNamespace('taskbar', 'http://schemas.microsoft.com/Start/2014/TaskbarLayout')
# Find the TaskbarPinList node
$taskbarPinListNode = $xml.SelectSingleNode('//taskbar:TaskbarPinList', $ns)
if ($null -eq $taskbarPinListNode) {
throw "TaskbarPinList node not found in $XmlFilePath"
}
# Create a new DesktopApp element
$newAppNode = $xml.CreateElement('DesktopApp', 'http://schemas.microsoft.com/Start/2014/TaskbarLayout')
$newAppNode.SetAttribute('DesktopApplicationLinkPath', $AppLinkPath)
# Append the new app to the TaskbarPinList
$taskbarPinListNode.AppendChild($newAppNode)
# Save the updated XML back to the file
$xml.Save($XmlFilePath)
}
catch {
Write-Host "[Error] Failed to add app to TaskbarPinList. Error: $_" -ForegroundColor Red
}
}
static [string] GetFileBase64([string] $path) {
<#
.SYNOPSIS
Converts a file to Base64 encoding.
.DESCRIPTION
This function reads the contents of a file and converts it to a Base64 string.
.PARAMETER path
The path of the file to be converted.
.EXAMPLE
GetFileBase64 -path "C:\path\to\file.txt"
This example converts the file located at "C:\path\to\file.txt" to Base64 encoding.
#>
try {
$file = [System.IO.File]::ReadAllBytes($path)
return [System.Convert]::ToBase64String($file)
}
catch {
throw $_.Exception.Message
}
}
static [void] RemoveFiles([hashtable[]] $fileList) {
<#
.SYNOPSIS
Removes specified files and directories.
.DESCRIPTION
This function removes the files and directories specified in the input parameter.
.PARAMETER fileList
An array of hashtables, each containing the 'path' and 'recursive' keys.
.EXAMPLE
$list = @(
@{path = "C:\Dell"; recursive = $True},
@{path = "C:\Intel"; recursive = $True},
@{path = "C:\PerfLogs"; recursive = $True}
)
RemoveFiles -fileList $list
#>
foreach ($item in $fileList) {
if (Test-Path $item.path) {
try {
if ($item.recursive) {
Remove-Item -Path $item.path -Recurse -Force -ErrorAction Stop
}
else {
Remove-Item -Path $item.path -Force -ErrorAction Stop
}
Write-Host "[OK] Successfully removed '$($item.path)'" -ForegroundColor Green
}
catch {
Write-Host "[Error] Failed to remove '$($item.path)': $_" -ForegroundColor Red
}
}
else {
Write-Host "[Warning] Path '$($item.path)' not found" -ForegroundColor Magenta
}
}
}
}
Function UpdateWallpaper([String] $Image) {
<#
.SYNOPSIS
Updates the wallpaper of the Windows 11 Sandbox.
.DESCRIPTION
This function updates the wallpaper of the Windows 11 Sandbox by calling the SystemParametersInfo function from the User32.dll.
.PARAMETER Image
The path of the image file to set as the wallpaper.
.EXAMPLE
UpdateWallpaper -Image "C:\Path\To\Image.jpg"
Updates the wallpaper of the Windows 11 Sandbox with the specified image.
.LINK
https://www.joseespitia.com/2017/09/15/set-wallpaper-powershell-function/
#>
Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
public class Params {
[DllImport("User32.dll",CharSet=CharSet.Unicode)]
public static extern int SystemParametersInfo (Int32 uAction, Int32 uParam, String lpvParam, Int32 fuWinIni);
}
'@
$SPI_SETDESKWALLPAPER = 0x0014
$UpdateIniFile = 0x01
$SendChangeEvent = 0x02
$fWinIni = $UpdateIniFile -bor $SendChangeEvent
$ret = [Params]::SystemParametersInfo($SPI_SETDESKWALLPAPER, 0, $Image, $fWinIni)
}
# Title: Remote Visual Studio Code development on a Windows 11 Sandbox VM
# Author: B. van Wetten
# Last Update: 2024-01-10
#
# Usage: PowerShell.exe -ExecutionPolicy Bypass { Invoke-RestMethod -Uri https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---vscode_sandbox.ps1 | Invoke-Expression }
# Start of script
Set-StrictMode -Version 1.0
Set-Variable -Name ProgressPreference -Value "SilentlyContinue" # Default is "Continue"
Set-Variable -Name ErrorActionPreference -Value "Stop" # Default is "Continue"
$global:VSCodeSandboxVersion = "1.0.0"
$global:TempFolder = "$env:Temp"
$global:TempFolderRemote = "C:\Users\WDAGUtilityAccount\AppData\Local\Temp"
$global:VSCodeSandboxUserProfile = "$env:UserProfile"
$global:VSCodeSandboxUserProfileRemote = "C:\Users\WDAGUtilityAccount"
$global:VSCodeSandboxPath = "$env:UserProfile\.vscode_sandbox"
$global:VSCodeSandboxPathRemote = "C:\Users\WDAGUtilityAccount\Desktop\VSCodeSandbox"
$global:VSCodeSandboxPathWorkspace = "$global:VSCodeSandboxPath\workspace"
$global:VSCodeSandboxPathWorkspaceRemote = "$global:VSCodeSandboxPathRemote\workspace"
$global:VSCodeSandboxScript = "$global:VSCodeSandboxPath\.setup\sandbox_vscode.ps1"
$global:VSCodeSandboxScriptRemote = "$global:VSCodeSandboxPathRemote\.setup\sandbox_vscode.ps1"
$global:OpenSSHMSI = "OpenSSH-Win64-v9.5.0.0.msi"
$global:OpenSSHURL = "https://github.com/PowerShell/Win32-OpenSSH/releases/download/v9.5.0.0p1-Beta/$global:OpenSSHMSI"
$global:Use1PasswordCLI = $false
try {
# Clear the screen
Clear-Host
# Check if Windows Sandbox is installed
try {
# Requires elevation: Get-WindowsOptionalFeature -Online -FeatureName Containers-DisposableClientVM -ErrorAction Stop | Out-Null
Get-Command -Name WindowsSandbox -ErrorAction Stop | Out-Null
Write-Host ("[OK] Windows Sandbox Feature is installed, continuing...") -ForegroundColor Green
}
catch {
Throw "Windows Sandbox Feature is not installed, exiting..."
}
# Check if Sandbox is already running, exit if it is
if ((Get-Process -Name WindowsSandbox -ErrorAction SilentlyContinue).Length -gt 0) {
Throw "Windows Sandbox is already running, exiting..."
}
# Check if VSCode is installed, exit if not
if (code --help) {
Write-Host ("[OK] Visual Studio Code is installed, continuing...") -ForegroundColor Green
}
else {
Throw "Visual Studio Code is not installed, exiting..."
}
# Check if OpenSSH client is installed, exit if not
if (-not (Get-Command ssh-keygen -ErrorAction SilentlyContinue)) {
Throw "OpenSSH client not found, exiting..."
}
# Check if .ssh folder exists, create if not
if (-not (Test-Path -Path $env:UserProfile\.ssh)) {
New-Item -ItemType Directory -Path $env:UserProfile\.ssh -Force | Out-Null
}
# Check if Remote-SSH extension is installed, install if not
if ((code --list-extensions | Select-String 'ms-vscode-remote.remote-ssh').Length -gt 0) {
Write-Host ("[OK] Remote-SSH extension is installed, continuing...") -ForegroundColor Green
}
else {
Write-Host ("[Info] Installing Remote-SSH extension...") -ForegroundColor DarkGray
code --install-extension ms-vscode-remote.remote-ssh | Out-Null
}
# Create a $global:VSCodeSandboxPath/.setup folder if it does not exist
if (-not (Test-Path -Path $global:VSCodeSandboxPath\.setup)) {
New-Item -Type Directory -Path $global:VSCodeSandboxPath\.setup -Force | Out-Null
Write-Host ("[Info] Created $global:VSCodeSandboxPath\.setup folder") -ForegroundColor DarkGray
}
else {
Write-Host ("[OK] '$global:VSCodeSandboxPath\.setup' already exists, continuing...") -ForegroundColor Green
}
# Create SSH keys for connecting to Windows Sandbox
if ($global:Use1PasswordCLI) {
# Check if 1Password CLI is installed, exit if not installed
if (-not (Get-Command op -ErrorAction SilentlyContinue)) {
Throw "1Password CLI not found, exiting..."
}
# Fetch ssh keys (op item list --tags vscode-sandbox,ssh --categories SSHKey)
# $id = (op item list --tags vscode-sandbox,ssh --categories SSHKey --format json | ConvertFrom-Json | Select-Object -First 1).id
try {
$id = Invoke-Command -ErrorAction SilentlyContinue -ScriptBlock { op item list --tags vscode-sandbox, ssh --categories SSHKey --format json | ConvertFrom-Json | Select-Object -First 1 }
Write-Host ("`$id = $id") -ForegroundColor DarkGray
# $id = (op item list --tags vscode-sandbox,ssh --categories SSHKey --format json -ErrorAction Stop | ConvertFrom-Json | Select-Object -First 1).id
Write-Host ("Found SSH keys in 1Password, importing private key and copying .pub file to C:\VSCodeSandbox...") -ForegroundColor Green
}
catch {
# Handle the error
Write-Output "An error occurred: $_"
}
}
else {
try {
if (-not (Test-Path -Path $env:UserProfile\.ssh\id_ed25519_vscode)) {
# Create SSH keys using ssh-keygen.exe
Invoke-Command -ScriptBlock { ssh-keygen.exe -t ed25519 -f $env:UserProfile\.ssh\id_ed25519_vscode -q -N "" } | Out-Null
# Check if ssh-agent is running and if it is running, add the SSH key to the agent
if ((Get-Service ssh-agent).Status -eq "Running") {
& (Get-Command ssh-add).Source $env:UserProfile\.ssh\id_ed25519_vscode *> $null
}
Write-Host ("[Info] Created SSH keys...") -ForegroundColor DarkGray
}
else {
Write-Host ("[OK] SSH Keys already exist, continuing...") -ForegroundColor Green
}
# Copy .gitconfig file to $global:VSCodeSandboxUserProfileRemote\.gitconfig
Write-Host ("[Info] Copying .gitconfig file to $global:VSCodeSandboxUserProfileRemote\.gitconfig...") -ForegroundColor DarkGray
Copy-Item $env:UserProfile\.gitconfig -Destination $global:VSCodeSandboxPath\.setup\.gitconfig -Force
# Copy public key to $global:VSCodeSandboxPath
Write-Host ("[Info] Copying public key to $global:VSCodeSandboxPath\.setup") -ForegroundColor DarkGray
Copy-Item $env:UserProfile\.ssh\id_ed25519_vscode.pub $global:VSCodeSandboxPath\.setup\sandbox_pubkey -Force:$true -Confirm:$false
}
catch {
Write-Host ("[Error] $($_.Exception.Message)") -ForegroundColor Red
Throw "Error creating SSH keys, exiting..."
}
}
# Remove previous sandbox_ip
if (Test-Path -Path "$global:VSCodeSandboxPath\.setup\sandbox_ip") {
Write-Host ("[Info] Removing previous $global:VSCodeSandboxPath\.setup\sandbox_ip file...") -ForegroundColor DarkGray
Remove-Item -Path "$global:VSCodeSandboxPath\.setup\sandbox_ip" -Force:$true -Confirm:$false
}
# Create a VSCode.wsb file in C:\Windows\Temp\VSCodeSandbox
$wsb = @"
<Configuration>
<VGpu>Enable</VGpu>
<Networking>Enable</Networking>
<MappedFolders>
<MappedFolder>
<HostFolder>$global:VSCodeSandboxUserProfile\.ssh</HostFolder>
<SandboxFolder>$global:VSCodeSandboxPathRemote\.setup\.ssh</SandboxFolder>
<ReadOnly>true</ReadOnly>
</MappedFolder>
<MappedFolder>
<HostFolder>$global:VSCodeSandboxPath</HostFolder>
<SandboxFolder>$global:VSCodeSandboxPathRemote</SandboxFolder>
<ReadOnly>false</ReadOnly>
</MappedFolder>
</MappedFolders>
<LogonCommand>
<Command>powershell -NoProfile -ExecutionPolicy Unrestricted -File "$global:VSCodeSandboxScriptRemote"</Command>
</LogonCommand>
</Configuration>
"@
$wsb | Out-File "$global:TempFolder\vscode.wsb" -Force -Confirm:$false
# Create the sandbox_vscode.ps1 for installation of OpenSSH Server, creation of local vscode admin account and vscodesshfile SSH Key
# Logging can be found in C:\Users\WDAGUtilityAccount\Desktop\VSCodeSandbox\.setup\sandbox_transcript if needed in the Windows Sandbox VM
$sandbox_vscode = @"
# Start of script
Start-Transcript $global:VSCodeSandboxPathRemote\.setup\sandbox_transcript
`$job = Start-Job -ArgumentList "$global:VSCodeSandboxPathRemote", "$global:VSCodeSandboxUserProfileRemote" -ScriptBlock {
Param(`$VSCodeSandboxPathRemote, `$VSCodeSandboxUserProfileRemote)
New-Item -Type Directory -Path "`$VSCodeSandboxPathRemote\.scoop" -Force | Out-Null
Invoke-RestMethod -Uri "https://get.scoop.sh" | Invoke-Expression
scoop config cache_path "`$VSCodeSandboxPathRemote\.scoop"
scoop config aria2-warning-enabled false
scoop config use_lessmsi `$true
scoop install lessmsi
scoop install 7zip aria2 git
scoop install nodejs python
reg import "`$VSCodeSandboxUserProfileRemote\scoop\apps\7zip\current\install-context.reg"
}
# Download OpenSSH MSI if it does not exist
if (-not (Test-Path "$global:VSCodeSandboxPathRemote\.setup\$global:OpenSSHMSI")) {
Invoke-WebRequest -Uri "$global:OpenSSHURL" -OutFile "$global:VSCodeSandboxPathRemote\.setup\$global:OpenSSHMSI"
}
# Install OpenSSH Client & Server
Start-Process -FilePath "msiexec.exe" -Verb RunAs -Wait -ArgumentList "/qn /i ``"$global:VSCodeSandboxPathRemote\.setup\$global:OpenSSHMSI``""
# Modify local user account WDAGUtilityAccount with password "vscode"
Set-LocalUser -Name WDAGUtilityAccount -PasswordNeverExpires 1 -Password ("vscode" | ConvertTo-SecureString -AsPlainText -Force)
# Create several folders
New-Item -Type Directory -Path C:\ProgramData\ssh -Force | Out-Null
New-Item -Type Directory -Path "$global:VSCodeSandboxPathRemote\.vscode-server" -Force | Out-Null
New-Item -Type Directory -Path "$global:VSCodeSandboxPathWorkspaceRemote" -Force | Out-Null
# Create symbolic link to .vscode-server folder
New-Item -Type SymbolicLink -Path "$global:VSCodeSandboxUserProfileRemote\.vscode-server" -Target "$global:VSCodeSandboxPathRemote\.vscode-server" -Force | Out-Null
# Copy .ssh folder to C:\Users\WDAGUtilityAccount and create symbolic link
Copy-Item -Path "$global:VSCodeSandboxPathRemote\.setup\.ssh" -Destination "$global:VSCodeSandboxUserProfileRemote" -Recurse -Force
# Create symbolic link to .gitconfig file
Copy-Item -Path "$global:VSCodeSandboxPathRemote\.setup\.gitconfig" -Destination "$global:VSCodeSandboxUserProfileRemote" -Force
# Copy public key to C:\ProgramData\ssh\administrators_authorized_keys
Copy-Item "$global:VSCodeSandboxPathRemote\.setup\sandbox_pubkey" C:\ProgramData\ssh\administrators_authorized_keys
# Store current ip address of Sandbox in $global:VSCodeSandboxPathRemote\.setup\sandbox_ip
(Get-NetIPAddress -InterfaceAlias Ethernet -AddressFamily IPv4).IPAddress | Out-File "$global:VSCodeSandboxPathRemote\.setup\sandbox_ip" -Force
# Wait for asynchronous job to finish
Receive-Job -Job `$job -Wait
Stop-Transcript
"@
$sandbox_vscode | Out-File $global:VSCodeSandboxScript -Force
# Start Windows Sandbox using the VSCode.wsb file
Write-Host ("[Info] Starting Windows Sandbox...") -ForegroundColor DarkGray
Start-Process -FilePath $(Get-Command -Name WindowsSandbox).Source -ArgumentList "$global:TempFolder\vscode.wsb" | Out-Null
# Wait for installation of OpenSSH server and creation of sandbox_ip file
$interval = 0
while (-not (Test-Path -Path $global:VSCodeSandboxPath\.setup\sandbox_ip)) {
# Perform modulo on $interval to check if it is a multiple of 5
if (($interval++ % 15) -eq 0) {
Write-Host ("[Info] Waiting for creation of $global:VSCodeSandboxPath\.setup\sandbox_ip file...") -ForegroundColor DarkGray
}
Start-Sleep -Seconds 1
if ($interval -gt 60) {
Throw "Error creating $global:VSCodeSandboxPath\.setup\sandbox_ip file, exiting..."
}
}
# Check each service for status "Running" and wait until all services are running (Get-Service -Name ssh*).Status -eq "Running"
$interval = 0
while ((Get-Service -Name ssh* | Where-Object { $_.Status -eq 'Running' }).Count -ge 1) {
# Perform modulo on $interval to check if it is a multiple of 5
if (($interval++ % 15) -eq 0) {
Write-Host ("[Info] Waiting for OpenSSH services to start...") -ForegroundColor DarkGray
}
Start-Sleep -Seconds 1
if ($interval -gt 60) {
Throw "Error starting OpenSSH services, exiting..."
}
}
# Retrieve current ip address of Sandbox from $global:VSCodeSandboxPath\.setup\sandbox_ip
$sandbox_ip = Get-Content $global:VSCodeSandboxPath\.setup\sandbox_ip
# If ip address is not in the 172.0.0.0/8 range, exit
if ($sandbox_ip -notmatch "^172\.\d{1,3}\.\d{1,3}\.\d{1,3}$") {
Throw "Error retrieving Sandbox IP address, exiting..."
}
Write-Host ("[OK] Installation done, continuing...") -ForegroundColor Green
# Check if .ssh\config file exists, if not create it
if (-not (Test-Path -Path $env:UserProfile\.ssh\config)) {
New-Item -ItemType File -Path $env:UserProfile\.ssh\config | Out-Null
}
try {
$newConfig = @"
# Auto-generated by vscode_sandbox.ps1 - Version $global:VSCodeSandboxVersion
# Last updated: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
# !!! - Do not edit this section manually - !!!
Host sandbox
User WDAGUtilityAccount
CheckHostIP no
Hostname $sandbox_ip
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
PreferredAuthentications publickey
IdentityFile $env:UserProfile\.ssh\id_ed25519_vscode
"@
$configPath = "$env:UserProfile\.ssh\config"
$configContent = Get-Content -Path $configPath -Raw
$pattern = "(?m)((?:^#.*$[\r\n]){3}Host sandbox[\r\n\s\S]+?)(?=(?:[\r\n\s]+^Host)|[\r\n\s]+\z)"
# Use regex to check if the configuration already exists
$configExists = $configContent -match $pattern
if ($configExists -and (Get-Variable Matches)) {
# Replace the existing configuration
$configContent = $configContent.Replace($Matches[0], "$newConfig")
}
else {
# Append the new configuration
$configContent += "`n$newConfig"
}
$configContent.TrimEnd() | Set-Content -Path "$configPath"
}
catch {
Write-Host ("[Error] $($_.Exception.Message)") -ForegroundColor Red
Throw "Error updating $env:UserProfile\.ssh\config file, exiting..."
}
# Start new VSCode session to Sandbox using SSH key
Write-Host ("[Info] Connecting to Windows Sandbox at $sandbox_ip...") -ForegroundColor DarkGray
& (Get-Command -Name code).Source --profile Sandbox --remote ssh-remote+WDAGUtilityAccount@sandbox $global:VSCodeSandboxPathWorkspaceRemote
}
catch {
Write-Host "[Error] $($_.Exception.Message)" -ForegroundColor Red
# Get sandbox process and kill it
$sandboxProcess = Get-Process -Name WindowsSandboxClient -ErrorAction SilentlyContinue
if ($sandboxProcess) {
Write-Host ("[Info] Killing Windows Sandbox process...") -ForegroundColor DarkGray
$sandboxProcess | Stop-Process -Force:$true -Confirm:$false
}
exit 1
}
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
# Title: Windows 11 Deployment Script
# Filename: win11.ps1
# Author: B. van Wetten
# Description: Deployment script for Windows 11 that also performs
# some basic configuration and removes Windows bloatware.
# Last Update: 2024-01-07
#
# Prerequisites: See: https://gist.github.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df#file-provisioning-md
#
# Download only: Invoke-WebRequest -Uri https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---win11.ps1 -OutFile $env:userprofile\Downloads\win11.ps1
# Usage (bootstrap): PowerShell.exe -ExecutionPolicy Bypass { Invoke-RestMethod -Uri https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---bootstrap.ps1 | Invoke-Expression }
# Start of script
Using module .\lib\utils.psm1
Set-StrictMode -Version 1.0
Set-Variable -Name ProgressPreference -Value "SilentlyContinue" # Default is "Continue"
Set-Variable -Name ErrorActionPreference -Value "Continue" # Default is "Continue"
$global:InstallWinGet = $true
$global:CreateTaskbarLayout = $true
[Main]::Start()
class Main {
static [void] Start() {
try {
$global:errorOccurred = $true
# Check to make sure script is run as administrator
Write-Host "[Info] Checking if script is running as administrator..." -ForegroundColor Blue
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal( [Security.Principal.WindowsIdentity]::GetCurrent() )
if (-Not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Throw "Script must be run as administrator"
}
# Winget installation (disabled)
if ($global:InstallWinGet) {
# Install WinGet
Write-Host "[Info] Downloading WinGet and its dependencies..." -ForegroundColor Blue
# Check if WinGet is already installed
try {
$wingetCommand = Get-Command winget -ErrorAction Stop
Write-Host "[Info] winget.exe is already installed." -ForegroundColor Blue
}
catch {
Write-Host "[Info] winget.exe is not installed. Installing..." -ForegroundColor Blue
# Download WinGet and its dependencies
[Utils]::DownloadFile("https://aka.ms/getwinget", "${env:USERPROFILE}\Downloads", "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle")
[Utils]::DownloadFile("https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx", "${env:USERPROFILE}\Downloads")
[Utils]::DownloadFile("https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.7.3/Microsoft.UI.Xaml.2.7.x64.appx", "${env:USERPROFILE}\Downloads")
Start-Process -FilePath "powershell.exe" -NoNewWindow -Wait -ArgumentList "-NoProfile { `$ProgressPreference = `"SilentlyContinue`"; Add-AppxPackage `$args[0] } -args `"${env:USERPROFILE}\Downloads\Microsoft.UI.Xaml.2.7.x64.appx`"" | Out-Null
Start-Process -FilePath "powershell.exe" -NoNewWindow -Wait -ArgumentList "-NoProfile { `$ProgressPreference = `"SilentlyContinue`"; Add-AppxPackage `$args[0] } -args `"${env:USERPROFILE}\Downloads\Microsoft.VCLibs.x64.14.00.Desktop.appx`"" | Out-Null
Start-Process -FilePath "powershell.exe" -NoNewWindow -Wait -ArgumentList "-NoProfile { `$ProgressPreference = `"SilentlyContinue`"; Add-AppxPackage `$args[0] } -args `"${env:USERPROFILE}\Downloads\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle`"" | Out-Null
}
Write-Host "[Info] Updating WinGet repository..." -ForegroundColor Blue
winget.exe source update *> $null
}
Write-Host "[Info] Removing bloatware" -ForegroundColor Blue
if ( -Not [Bloatware]::Remove() ) {
Write-Host "[Error] Failed to remove bloatware" -ForegroundColor Red
}
Write-Host "[Info] Installing apps" -ForegroundColor Blue
if ( -Not [Apps]::Install() ) {
Write-Host "[Error] Failed to install apps" -ForegroundColor Red
}
Write-Host "[Info] Enabling Windows features" -ForegroundColor Blue
if ( -Not [WindowsFeatures]::Enable() ) {
Write-Host "[Error] Failed to enable Windows features" -ForegroundColor Red
}
Write-Host "[Info] Configuring Windows services" -ForegroundColor Blue
if ( -Not [WindowsServices]::Configure() ) {
Write-Host "[Error] Failed to configure Windows services" -ForegroundColor Red
}
Write-Host "[Info] Setting up Windows Subsystem for Linux (WSL2)" -ForegroundColor Blue
if ( -Not [WSL2]::Setup() ) {
Write-Host "[Error] Failed to set up WSL2" -ForegroundColor Red
}
Write-Host "[Info] Configuring Windows registry" -ForegroundColor Blue
if ( -Not [Registry]::ConfigureWindows() ) {
Write-Host "[Error] Failed to configure Windows registry" -ForegroundColor Red
}
Write-Host "[Info] Configuring network profile (Private)" -ForegroundColor Blue
Set-NetConnectionProfile -InterfaceAlias $(Get-NetConnectionProfile).InterfaceAlias -NetworkCategory "Private"
Write-Host "[Info] Performing cleanup" -ForegroundColor Blue
$list = @(
@{path = "C:\Dell"; recursive = $True},
@{path = "C:\Intel"; recursive = $True},
@{path = "C:\PerfLogs"; recursive = $True}
)
[Utils]::RemoveFiles($list)
Write-Host "[Info] Restarting explorer.exe" -ForegroundColor Blue
[Utils]::RestartExplorer()
$global:errorOccurred = $false
}
catch {
Write-Host "[Error] $_" -ForegroundColor Red
}
finally {
if ($global:errorOccurred) {
# Wait for user input
[Utils]::Pause("Press any key to exit")
}
else {
[Utils]::Pause("Press any key to exit", 3)
}
}
}
}
class Bloatware {
static [string[]] $list = @(
"MicrosoftTeams"
"Microsoft.WindowsAlarms"
"Microsoft.windowscommunicationsapps"
"Microsoft.Todos"
"Microsoft.BingFinance"
"Microsoft.3DBuilder"
"Microsoft.BingNews"
"Microsoft.BingSports"
"Microsoft.BingWeather"
"Microsoft.CommsPhone"
"Microsoft.Getstarted"
"Microsoft.WindowsMaps"
"*MarchofEmpires*"
"Microsoft.GetHelp"
"Microsoft.Messaging"
"*Minecraft*"
"Microsoft.OutlookForWindows"
"Microsoft.MicrosoftOfficeHub"
"Microsoft.OneConnect"
"Microsoft.WindowsPhone"
"Microsoft.Windows.Photos"
"Microsoft.WindowsSoundRecorder"
"*Solitaire*"
"Microsoft.MicrosoftStickyNotes"
"Microsoft.Office.Sway"
"Microsoft.XboxApp"
"Microsoft.XboxIdentityProvider"
"Microsoft.ZuneMusic"
"Microsoft.ZuneVideo"
"Microsoft.NetworkSpeedTest"
"Microsoft.FreshPaint"
"Microsoft.Print3D"
"Microsoft.People*"
"Microsoft.Microsoft3DViewer"
"Microsoft.MixedReality.Portal*"
"Clipchamp.Clipchamp"
"*Skype*"
"*Autodesk*"
"*BubbleWitch*"
"king.com*"
"G5*"
"*Dell*"
"*Facebook*"
"*Keeper*"
"*Netflix*"
"*Twitter*"
"*Plex*"
# "XBox*"
# "Microsoft.XboxGameOverlay"
# "Microsoft.XboxGamingOverlay"
)
static [bool] Remove() {
try {
foreach ($app in [Bloatware]::list) {
if ([Utils]::RemoveApp($app)) {
Write-Host "[OK] Removed $app" -ForegroundColor Green
} else {
Write-Host "[Error] Failed to remove $app" -ForegroundColor Red
}
}
return $true
} catch {
Write-Host "[Error] Error: $_" -ForegroundColor Red
return $false
}
}
}
class Apps {
static [hashtable[]] $list = @(
@{id = "Git.Git" },
@{id = "gerardog.gsudo" },
@{id = "Microsoft.VisualStudioCode" },
@{id = "Microsoft.WindowsTerminal" },
@{id = "GPSoftware.DirectoryOpus" },
@{id = "RoyalApps.RoyalTS.V7" },
@{id = "Docker.DockerDesktop" },
@{id = "tailscale.tailscale" },
@{id = "WireGuard.WireGuard" },
@{id = "AgileBits.1Password" },
@{id = "AgileBits.1Password.CLI" },
@{id = "evernote.evernote" },
@{id = "eMClient.eMClient" },
@{id = "Valve.Steam" },
@{id = "TechPowerUp.NVCleanstall" },
@{id = "KurtZimmermann.RegCool" },
@{id = "9NKSQGP7F2NH"; name = "WhatsApp" }
# @{id = "JanDeDobbeleer.OhMyPosh" },
# @{id = "EpicGames.EpicGamesLauncher" }
# @{id = "Microsoft.Office"; override = "/configure ""C:\Users\bas\Downloads\Office 365 config.xml""" }
# @{id = "9PD11RQ8QC9K"; name = "Dell Power Manager" }
# @{id = "Microsoft.PowerShell" },
# @{id = "Spotify.Spotify"; asUser = $true }
)
static [bool] Install() {
try {
foreach ($app in [Apps]::list) {
if ($app.id) {
$appName = $app.id
} else {
$appName = $app.name
}
if ($app.name) {
$appDisplayName = $app.name
} else {
$appDisplayName = $app.id
}
$listApp = $(winget list --exact -q "$appName")
if (![String]::Join("", $listApp).Contains($appName)) {
if ( -Not $app.asUser) {
Write-Host "[Info] Attempting to install globally '$($appDisplayName)' " -NoNewline -ForegroundColor Blue
if ($app.override) {
winget install -h --accept-source-agreements --accept-package-agreements "$appName" --override "$($app.override)" | Out-Null
} else {
winget install -h --accept-source-agreements --accept-package-agreements "$appName" | Out-Null
}
if ($?) {
Write-Host "success" -ForegroundColor Green
} else {
Write-Host "failed" -ForegroundColor Red
}
} else {
Write-Host "`[Info] Attempting to install '$($appDisplayName)' for user '$([System.Environment]::UserName)' " -NoNewline -ForegroundColor Blue
$credential = Get-WinCredential -Title $($appDisplayName) -Message "Please provide credentials for installation" -UserName $([System.Environment]::UserName)
$result = Start-Job -Credential $credential -ArgumentList "$appName" -ScriptBlock {
param([string] $appName)
if ($app.override) {
winget install -h --scope user --accept-source-agreements --accept-package-agreements $appName --override "$($app.override)" | Out-Null
} else {
winget install -h --scope user --accept-source-agreements --accept-package-agreements $appName | Out-Null
}
return [bool] $?
} | Wait-Job | Receive-Job
if ([bool] $result) {
Write-Host "success" -ForegroundColor Green
} else {
Write-Host "failed" -ForegroundColor Red
}
}
} else {
Write-host "[Info] Already installed, skipping '$($appDisplayName)'" -ForegroundColor Blue
}
}
return $true
} catch {
Write-Host "[Error] Error: $_" -ForegroundColor Red
return $false
}
}
}
class WindowsFeatures {
static [hashtable[]] $list = @(
@{name = "OpenSSH.Client~~~~0.0.1.0"; isCapability = $True },
@{name = "OpenSSH.Server~~~~0.0.1.0"; isCapability = $True },
@{name = "NetFx3" },
@{name = "VirtualMachinePlatform" },
@{name = "Microsoft-Windows-Subsystem-Linux" },
@{name = "Containers-DisposableClientVM" }
);
static [bool] Enable() {
try {
foreach ($feature in [WindowsFeatures]::list) {
if ($($feature.isCapability)) {
If ((Get-WindowsCapability -Online -Name $($feature.name)).State -ne 'Installed') {
Write-Host "[Info] Attempting to install Windows capability '$($feature.name)'" -ForegroundColor Blue
Add-WindowsCapability -Online -Name $($feature.name)
} else {
Write-Host "[Info] Windows capability '$($feature.name)' already installed" -ForegroundColor Blue
}
} else {
If ((Get-WindowsOptionalFeature -Online -FeatureName $($feature.name)).State -ne 'Enabled') {
Write-Host "[Info] Attempting to enable Windows optional feature '$($feature.name)'" -ForegroundColor Blue
Enable-WindowsOptionalFeature -Online -FeatureName $($feature.name) -All -NoRestart -ErrorAction SilentlyContinue
} else {
Write-Host "[Info] Windows optional feature '$($feature.name)' already enabled" -ForegroundColor Blue
}
}
}
return $True
} catch {
Write-Host $_
return $False
}
}
}
class Registry {
static [hashtable[]] $list = @(
@{name = "HideTaskbarSearch" }
@{name = "DisableTelemetry" }
@{name = "DisableEdgeHubSidebar" }
@{name = "DisableWebSearch" }
@{name = "DisableMapUpdates" }
@{name = "HideChatIcon" }
@{name = "DisableWindowsWidgets" }
@{name = "SetLongDateFormat" }
@{name = "SetShortDateFormat" }
)
static [bool] ConfigureWindows() {
try {
foreach ($method in [Registry]::list) {
try {
$expr = "[Registry]::$($method.name)()"
Invoke-Expression $expr
} catch {
Write-Host "[Error] Unable to invoke static method [Registry]::$($method.name)()" -ForegroundColor Red
}
}
# Write-Host "[Info] Restore pinned taskbar icons..." -ForegroundColor Blue
# [Utils]::RestorePinnedTaskbarIcons()
# Create appropriate taskbar layout (enabled)
if ($global:CreateTaskbarLayout) {
# <?xml version="1.0" encoding="utf-8"?>
# <LayoutModificationTemplate
# xmlns="http://schemas.microsoft.com/Start/2014/LayoutModification"
# xmlns:defaultlayout="http://schemas.microsoft.com/Start/2014/FullDefaultLayout"
# xmlns:start="http://schemas.microsoft.com/Start/2014/StartLayout"
# xmlns:taskbar="http://schemas.microsoft.com/Start/2014/TaskbarLayout"
# Version="1">
# <CustomTaskbarLayoutCollection>
# <defaultlayout:TaskbarLayout>
# <taskbar:TaskbarPinList>
# <taskbar:DesktopApp DesktopApplicationID="Microsoft.Windows.Explorer" />
# <taskbar:DesktopApp DesktopApplicationID="Microsoft.Windows.Terminal" />
# <taskbar:DesktopApp DesktopApplicationLinkPath="%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\Microsoft Edge.lnk" />
# </taskbar:TaskbarPinList>
# </defaultlayout:TaskbarLayout>
# </CustomTaskbarLayoutCollection>
# </LayoutModificationTemplate>
Write-Host "[Info] Creating taskbar layout" -ForegroundColor Blue
$taskbarLayoutModificationFileData = 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjxMYXlvdXRNb2RpZmljYXRpb25UZW1wbGF0ZQ0KICAgIHhtbG5zPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL1N0YXJ0LzIwMTQvTGF5b3V0TW9kaWZpY2F0aW9uIg0KICAgIHhtbG5zOmRlZmF1bHRsYXlvdXQ9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vU3RhcnQvMjAxNC9GdWxsRGVmYXVsdExheW91dCINCiAgICB4bWxuczpzdGFydD0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS9TdGFydC8yMDE0L1N0YXJ0TGF5b3V0Ig0KICAgIHhtbG5zOnRhc2tiYXI9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vU3RhcnQvMjAxNC9UYXNrYmFyTGF5b3V0Ig0KICAgIFZlcnNpb249IjEiPg0KICA8Q3VzdG9tVGFza2JhckxheW91dENvbGxlY3Rpb24+DQogICAgPGRlZmF1bHRsYXlvdXQ6VGFza2JhckxheW91dD4NCiAgICAgIDx0YXNrYmFyOlRhc2tiYXJQaW5MaXN0Pg0KICAgICAgICA8dGFza2JhcjpEZXNrdG9wQXBwIERlc2t0b3BBcHBsaWNhdGlvbklEPSJNaWNyb3NvZnQuV2luZG93cy5FeHBsb3JlciIgLz4NCiAgICAgICAgPHRhc2tiYXI6RGVza3RvcEFwcCBEZXNrdG9wQXBwbGljYXRpb25JRD0iTWljcm9zb2Z0LldpbmRvd3MuVGVybWluYWwiIC8+DQogICAgICAgIDx0YXNrYmFyOkRlc2t0b3BBcHAgRGVza3RvcEFwcGxpY2F0aW9uTGlua1BhdGg9IiVQUk9HUkFNREFUQSVcTWljcm9zb2Z0XFdpbmRvd3NcU3RhcnQgTWVudVxQcm9ncmFtc1xNaWNyb3NvZnQgRWRnZS5sbmsiIC8+DQogICAgICA8L3Rhc2tiYXI6VGFza2JhclBpbkxpc3Q+DQogICAgPC9kZWZhdWx0bGF5b3V0OlRhc2tiYXJMYXlvdXQ+DQogPC9DdXN0b21UYXNrYmFyTGF5b3V0Q29sbGVjdGlvbj4NCjwvTGF5b3V0TW9kaWZpY2F0aW9uVGVtcGxhdGU+DQo='
[Utils]::SetTaskbarLayout("$env:UserProfile", $taskbarLayoutModificationFileData)
}
Write-Host "[Info] Restore desktop icons..." -ForegroundColor Blue
[DesktopIcons]::RestoreDesktopIcons()
Write-Host "[Info] Setting other registry values..." -ForegroundColor Blue
[Utils]::RestoreRegistry([OtherRegistrySettings]::registry)
return $true
} catch {
Write-Error $_
return $false
}
}
static [void] HideTaskbarSearch() {
Write-Host "[Info] Hiding Taskbar Search box / button..." -ForegroundColor Blue
Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Search" -Name "SearchboxTaskbarMode" -Type DWord -Value 0
}
static [void] ShowTaskbarSearchIcon() {
Write-Host "[Info] Showing Taskbar Search icon..." -ForegroundColor Blue
Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Search" -Name "SearchboxTaskbarMode" -Type DWord -Value 1
}
static [void] ShowTaskbarSearchBox() {
Write-Host "[Info] Showing Taskbar Search box..." -ForegroundColor Blue
Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Search" -Name "SearchboxTaskbarMode" -Type DWord -Value 2
}
# Disable Telemetry
static [void] DisableTelemetry() {
Write-Host "[Info] Disabling Telemetry..." -ForegroundColor Blue
Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\DataCollection" -Name "AllowTelemetry" -Type DWord -Value 0
Set-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\DataCollection" -Name "AllowTelemetry" -Type DWord -Value 0
Set-ItemProperty -Path "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Policies\DataCollection" -Name "AllowTelemetry" -Type DWord -Value 0
Set-ItemProperty -Path "HKCU:\Software\Policies\Microsoft\Windows\CloudContent" -Name "DisableTailoredExperiencesWithDiagnosticData" -Type DWord -Value 0
Get-ScheduledTask -TaskPath "\Microsoft\Windows\Application Experience\" -TaskName "Microsoft Compatibility Appraiser" -ErrorAction SilentlyContinue | Disable-ScheduledTask | Out-Null
Get-ScheduledTask -TaskPath "\Microsoft\Windows\Application Experience\" -TaskName "ProgramDataUpdater" -ErrorAction SilentlyContinue | Disable-ScheduledTask | Out-Null
Get-ScheduledTask -TaskPath "\Microsoft\Windows\Autochk\" -TaskName "Proxy" -ErrorAction SilentlyContinue | Disable-ScheduledTask | Out-Null
Get-ScheduledTask -TaskPath "\Microsoft\Windows\Customer Experience Improvement Program\" -TaskName "Consolidator" -ErrorAction SilentlyContinue | Disable-ScheduledTask | Out-Null
Get-ScheduledTask -TaskPath "\Microsoft\Windows\Customer Experience Improvement Program\" -TaskName "UsbCeip" -ErrorAction SilentlyContinue | Disable-ScheduledTask | Out-Null
Get-ScheduledTask -TaskPath "\Microsoft\Windows\DiskDiagnostic\" -TaskName "Microsoft-Windows-DiskDiagnosticDataCollector" -ErrorAction SilentlyContinue | Disable-ScheduledTask | Out-Null
}
# Enable Telemetry
static [void] EnableTelemetry() {
Write-Host "[Info] Enabling Telemetry..." -ForegroundColor Blue
Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\DataCollection" -Name "AllowTelemetry" -Type DWord -Value 3
Set-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\DataCollection" -Name "AllowTelemetry" -Type DWord -Value 3
Set-ItemProperty -Path "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Policies\DataCollection" -Name "AllowTelemetry" -Type DWord -Value 3
Set-ItemProperty -Path "HKCU:\Software\Policies\Microsoft\Windows\CloudContent" -Name "DisableTailoredExperiencesWithDiagnosticData" -Type DWord -Value 1
Get-ScheduledTask -TaskPath "\Microsoft\Windows\Application Experience\" -TaskName "Microsoft Compatibility Appraiser" -ErrorAction SilentlyContinue | Enable-ScheduledTask | Out-Null
Get-ScheduledTask -TaskPath "\Microsoft\Windows\Application Experience\" -TaskName "ProgramDataUpdater" -ErrorAction SilentlyContinue | Enable-ScheduledTask | Out-Null
Get-ScheduledTask -TaskPath "\Microsoft\Windows\Autochk\" -TaskName "Proxy" -ErrorAction SilentlyContinue | Enable-ScheduledTask | Out-Null
Get-ScheduledTask -TaskPath "\Microsoft\Windows\Customer Experience Improvement Program\" -TaskName "Consolidator" -ErrorAction SilentlyContinue | Enable-ScheduledTask | Out-Null
Get-ScheduledTask -TaskPath "\Microsoft\Windows\Customer Experience Improvement Program\" -TaskName "UsbCeip" -ErrorAction SilentlyContinue | Enable-ScheduledTask | Out-Null
Get-ScheduledTask -TaskPath "\Microsoft\Windows\DiskDiagnostic\" -TaskName "Microsoft-Windows-DiskDiagnosticDataCollector" -ErrorAction SilentlyContinue | Enable-ScheduledTask | Out-Null
}
# Disable Web Search in Start Menu
static [void] DisableWebSearch() {
Write-Host "[Info] Disabling Bing Search in Start Menu..." -ForegroundColor Blue
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Search" -Name "BingSearchEnabled" -Type DWord -Value 0
If (!(Test-Path "HKLM:\Software\Policies\Microsoft\Windows\Windows Search")) {
New-Item -Path "HKLM:\Software\Policies\Microsoft\Windows\Windows Search" -Force | Out-Null
}
Set-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\Windows Search" -Name "DisableWebSearch" -Type DWord -Value 1
}
# Enable Web Search in Start Menu
static [void] EnableWebSearch() {
Write-Host "[Info] Enabling Bing Search in Start Menu..." -ForegroundColor Blue
Remove-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Search" -Name "BingSearchEnabled" -ErrorAction SilentlyContinue
Remove-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\Windows Search" -Name "DisableWebSearch" -ErrorAction SilentlyContinue
}
# Disable Edge HubSidebar
static [void] DisableEdgeHubSidebar() {
Write-Host "[Info] Disabling Edge Hub Sidebar..." -ForegroundColor Blue
If (!(Test-Path "HKLM:\Software\Policies\Microsoft\Edge")) {
New-Item -Path "HKLM:\Software\Policies\Microsoft\Edge" -Force | Out-Null
}
Set-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Edge" -Name "HubsSidebarEnabled" -Type DWord -Value 0
}
# Enable Edge HubSidebar
static [void] EnableEdgeHubSidebar() {
Write-Host "[Info] Enabling Edge Hub Sidebar..." -ForegroundColor Blue
If (!(Test-Path "HKLM:\Software\Policies\Microsoft\Edge")) {
New-Item -Path "HKLM:\Software\Policies\Microsoft\Edge" -Force | Out-Null
}
Set-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Edge" -Name "HubsSidebarEnabled" -Type DWord -Value 1
}
# Disable Windows 11 widgets
static [void] DisableWindowsWidgets() {
Write-Host "[Info] Disabling Windows 11 widgets..." -ForegroundColor Blue
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -Name "TaskbarDa" -Type DWord -Value 0
}
# Enable Windows 11 widgets
static [void] EnableWindowsWidgets() {
Write-Host "[Info] Enabling Windows 11 widgets..." -ForegroundColor Blue
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -Name "TaskbarDa" -Type DWord -Value 1
}
# Disable automatic Maps updates
static [void] DisableMapUpdates() {
Write-Host "[Info] Disabling automatic Maps updates..." -ForegroundColor Blue
Set-ItemProperty -Path "HKLM:\SYSTEM\Maps" -Name "AutoUpdateEnabled" -Type DWord -Value 0
}
# Enable automatic Maps updates
static [void] EnableMapUpdates() {
Write-Host "[Info] Enabling automatic Maps updates..." -ForegroundColor Blue
Remove-ItemProperty -Path "HKLM:\SYSTEM\Maps" -Name "AutoUpdateEnabled" -ErrorAction SilentlyContinue
}
# Hide chat icon
static [void] HideChatIcon() {
Write-Host "[Info] Hiding taskbar Chat icon..." -ForegroundColor Blue
Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -Name "TaskbarMn" -Type DWord -Value 0
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows" -Name "ChatIcon" -Type DWord -Value 0x03
}
# Show chat icon
static [void] ShowChatIcon() {
Write-Host "[Info] Showing taskbar Chat icon..." -ForegroundColor Blue
Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -Name "TaskbarMn" -Type DWord -Value 1
Remove-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows" -Name "ChatIcon" -ErrorAction SilentlyContinue
}
# Disable Windows Desktop Spotlight (DisableWindowsSpotlightFeatures)
static [void] DisableWindowsSpotlightFeatures() {
Write-Host "[Info] Disabling Windows Desktop Spotlight..." -ForegroundColor Blue
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CloudContent" -Name "DisableWindowsSpotlightFeatures" -Type DWord -Value 1
}
# Enable Windows Desktop Spotlight (DisableWindowsSpotlightFeatures)
static [void] EnableWindowsSpotlightFeatures() {
Write-Host "[Info] Enabling Windows Desktop Spotlight..." -ForegroundColor Blue
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CloudContent" -Name "DisableWindowsSpotlightFeatures" -Type DWord -Value 0
}
# Enable 'show file extensions' option
static [void] ShowFileExtensions() {
Write-Host "[Info] Enabling 'show file extensions' option..." -ForegroundColor Blue
Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -Name "HideFileExt" -Type DWord -Value 0
}
# Disable 'show file extensions' option
static [void] HideFileExtensions() {
Write-Host "[Info] Disabling 'show file extensions' option..." -ForegroundColor Blue
Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -Name "HideFileExt" -Type DWord -Value 1
}
static [void] SetShortTimeFormat() {
Write-Host "[Info] Setting short time format (HH:mm:ss)..." -ForegroundColor Blue
Set-ItemProperty -Path "HKCU:\Control Panel\International" -name sShortTime -value "hh:mm:ss"
}
static [void] SetLongTimeFormat() {
Write-Host "[Info] Setting long time format (HH:mm:ss)..." -ForegroundColor Blue
Set-ItemProperty -Path "HKCU:\Control Panel\International" -name sTimeFormat -value "hh:mm:ss"
}
static [void] SetLongDateFormat() {
Write-Host "[Info] Setting long date format (dddd, MMMM d, yyyy)..." -ForegroundColor Blue
Set-ItemProperty -Path "HKCU:\Control Panel\International" -name sLongDate -value "dddd, MMMM d, yyyy"
}
static [void] SetShortDateFormat() {
Write-Host "[Info] Setting short date format (d-M-yyyy)..." -ForegroundColor Blue
Set-ItemProperty -Path "HKCU:\Control Panel\International" -name sShortDate -value "d-M-yyyy"
}
}
class WindowsServices {
static [hashtable[]] $list = @(
@{name = "ssh-agent"; startupType = "disabled"; start = $False }
@{name = "KAPSService"; startupType = "disabled"; start = $False }
@{name = "Killer Analytics Service"; startupType = "disabled"; start = $False }
@{name = "Killer Network Service"; startupType = "disabled"; start = $False }
@{name = "KNDBWM"; startupType = "disabled"; start = $False }
)
static [bool] Configure() {
try {
foreach ($service in [WindowsServices]::list) {
$displayName = $(Get-service -Name $service.name).DisplayName
if ($?) {
Set-Service $service.name -StartupType $service.startupType
if ($service.start) {
Write-Host "[Info] Attempt to start service '$displayName': " -NoNewline -ForegroundColor Blue
Start-Service $service.name -WarningAction SilentlyContinue
if ($?) {
Write-Host "success" -ForegroundColor Green
} else {
Write-Host "failed" -ForegroundColor Red
}
} else {
Write-Host "[Info] Attempt to stop service '$displayName': " -NoNewline -ForegroundColor Blue
Stop-Service $service.name -WarningAction SilentlyContinue
if ($?) {
Write-Host "success" -ForegroundColor Green
} else {
Write-Host "failed" -ForegroundColor Red
}
}
}
}
return $true
} catch {
return $false
}
}
}
class WSL2 {
static [bool] Setup() {
try {
wsl --set-default-version 2
wsl --install --no-launch --distribution Ubuntu-22.04
return $True
} catch {
return $False
}
}
}
class OtherRegistrySettings {
<#
- Locale registry settings for Dutch (Netherlands)
#>
static [hashtable[]] $registry = @(
@{path = "HKCU:\Control Panel\International\Geo"; name = "Nation"; propertyType = "String"; data = "176" }
@{path = "HKCU:\Control Panel\International\Geo"; name = "Name"; propertyType = "String"; data = "NL" }
@{path = "HKCU:\Control Panel\International"; name = "Locale"; propertyType = "String"; data = "00000413" }
@{path = "HKCU:\Control Panel\International"; name = "LocaleName"; propertyType = "String"; data = "nl-NL" }
@{path = "HKCU:\Control Panel\International"; name = "s1159"; propertyType = "String"; data = "" }
@{path = "HKCU:\Control Panel\International"; name = "s2359"; propertyType = "String"; data = "" }
@{path = "HKCU:\Control Panel\International"; name = "sCurrency"; propertyType = "String"; data = "€" }
@{path = "HKCU:\Control Panel\International"; name = "sDate"; propertyType = "String"; data = "-" }
@{path = "HKCU:\Control Panel\International"; name = "sDecimal"; propertyType = "String"; data = "," }
@{path = "HKCU:\Control Panel\International"; name = "sLanguage"; propertyType = "String"; data = "NLD" }
@{path = "HKCU:\Control Panel\International"; name = "sList"; propertyType = "String"; data = ";" }
@{path = "HKCU:\Control Panel\International"; name = "sLongDate"; propertyType = "String"; data = "dddd d MMMM yyyy" }
@{path = "HKCU:\Control Panel\International"; name = "sMonDecimalSep"; propertyType = "String"; data = "," }
@{path = "HKCU:\Control Panel\International"; name = "sMonThousandSep"; propertyType = "String"; data = "." }
@{path = "HKCU:\Control Panel\International"; name = "sShortDate"; propertyType = "String"; data = "d-M-yyyy" }
@{path = "HKCU:\Control Panel\International"; name = "sThousand"; propertyType = "String"; data = "." }
@{path = "HKCU:\Control Panel\International"; name = "iCountry"; propertyType = "String"; data = "31" }
@{path = "HKCU:\Control Panel\International"; name = "iCurrency"; propertyType = "String"; data = "2" }
@{path = "HKCU:\Control Panel\International"; name = "iFirstWeekOfYear"; propertyType = "String"; data = "2" }
@{path = "HKCU:\Control Panel\International"; name = "iNegCurr"; propertyType = "String"; data = "12" }
@{path = "HKCU:\Control Panel\International"; name = "sShortTime"; propertyType = "String"; data = "HH:mm" }
@{path = "HKCU:\Control Panel\International"; name = "sTimeFormat"; propertyType = "String"; data = "HH:mm:ss" }
)
}
class Taskbar {
static [hashtable[]] $registry = @(
@{path = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Taskband"; name = "Favorites"; propertyType = "Binary"; data = "001206000014001f809bd434424502f34db7803893943456e1fc050000fa0441505053e80408000300000000000000780200003153505355284c9f799f394ba8d0e1d42de1d5f36100000011000000001f000000280000004d006900630072006f0073006f00660074002e00570069006e0064006f00770073005400650072006d0069006e0061006c005f003800770065006b0079006200330064003800620062007700650000001100000027000000000b000000ffff0000110000000e00000000130000000200000011000000190000000013000000010000008500000015000000001f000000390000004d006900630072006f0073006f00660074002e00570069006e0064006f00770073005400650072006d0069006e0061006c005f0031002e00310038002e0033003100380031002e0030005f007800360034005f005f003800770065006b00790062003300640038006200620077006500000000006900000005000000001f0000002c0000004d006900630072006f0073006f00660074002e00570069006e0064006f00770073005400650072006d0069006e0061006c005f003800770065006b0079006200330064003800620062007700650021004100700070000000bd0000000f000000001f0000005600000043003a005c00500072006f006700720061006d002000460069006c00650073005c00570069006e0064006f007700730041007000700073005c004d006900630072006f0073006f00660074002e00570069006e0064006f00770073005400650072006d0069006e0061006c005f0031002e00310038002e0033003100380031002e0030005f007800360034005f005f003800770065006b0079006200330064003800620062007700650000001d000000200000000048000000e55f9bd0c945b542a874c452684c8cb400000000cd010000315350534d0bd48669903c44819a2a54090dccec4d0000000c000000001f0000001d00000049006d0061006700650073005c0053007100750061007200650031003500300078003100350030004c006f0067006f002e0070006e006700000000004900000002000000001f0000001b00000049006d0061006700650073005c00530071007500610072006500340034007800340034004c006f0067006f002e0070006e00670000000000490000000d000000001f0000001b00000049006d0061006700650073005c00570069006400650033003100300078003100350030004c006f0067006f002e0070006e00670000000000110000000400000000130000000078d4ff11000000050000000013000000ffffffff3d00000013000000001f0000001500000049006d0061006700650073005c004c006100720067006500540069006c0065002e0070006e00670000000000110000000e0000000013000000a1040000250000000b000000001f000000090000005400650072006d0069006e0061006c00000000003d00000014000000001f0000001500000049006d0061006700650073005c0053006d0061006c006c00540069006c0065002e0070006e00670000000000000000003100000031535053b1166d44ad8d7048a748402ea43d788c15000000640000000015000000f70400000000000000000000410000003153505330f125b7ef471a10a5f102608c9eebac250000000a000000001f000000090000005400650072006d0069006e0061006c0000000000000000002d00000031535053b377ed0d14c66c45ae5b285b38d7b01b110000000700000000130000000000000000000000000000000000120000002b00efbe11da88d7203bda010005640000001d00efbe02004d006900630072006f0073006f00660074002e00570069006e0064006f00770073005400650072006d0069006e0061006c005f003800770065006b00790062003300640038006200620077006500210041007000700000000005220000001e00efbe02005500730065007200500069006e006e006500640000000005640000001d00efbe02004d006900630072006f0073006f00660074002e00570069006e0064006f00770073005400650072006d0069006e0061006c005f003800770065006b00790062003300640038006200620077006500210041007000700000000005000000760100003a001f80c827341f105c1042aa032ee45287d668260001002600efbe12000000053783fdb757d90127e55b24b857d9010bd0b1309a5cd901140056003100000000007556d4a411005461736b42617200400009000400efbe70563d1c7656cc3d2e000000498501000000010000000000000000000000000000001715e0005400610073006b0042006100720000001600e4003200790500007656454520004449524543547e312e4c4e4b0000560009000400efbe76564545765645452e000000e501060000002a0000000000000000000000000000009ae23e004400690072006500630074006f007200790020004f007000750073002e006c006e006b0000001c00120000002b00efbe2b08b3309a5cd9011c00220000001e00efbe02005500730065007200500069006e006e006500640000001c003e0000001d00efbe02004700500053006f006600740077006100720065002e004400690072006500630074006f00720079004f0070007500730000001c00000000520100003a001f80c827341f105c1042aa032ee45287d668260001002600efbe12000000cf27cefc0a3bda0168fa3d2e0b3bda01214f580c213bda01140056003100000000009e57d36811005461736b42617200400009000400efbe9e57c9539e57d3682e000000c2c00100000001000000000000000000000000000000fad900015400610073006b0042006100720000001600c0003200860900009e57935320004d4943524f537e312e4c4e4b0000560009000400efbe9e57ac639e57d9682e000000dc50020000000a000000000000000000000000000000f9762a014d006900630072006f0073006f0066007400200045006400670065002e006c006e006b0000001c00120000002b00efbe214f580c213bda011c00220000001e00efbe02005500730065007200500069006e006e006500640000001c001a0000001d00efbe02004d005300450064006700650000001c00000000500100003a001f80c827341f105c1042aa032ee45287d668260001002600efbe12000000cf27cefc0a3bda0168fa3d2e0b3bda012b82f657263bda01140056003100000000009e57936d11005461736b42617200400009000400efbe9e57c9539e57936d2e000000c2c00100000001000000000000000000000000000000a6be39005400610073006b0042006100720000001600be003200500400009e5703562000454d434c49457e312e4c4e4b00004c0009000400efbe9e575b679e57966d2e000000ad520200000007000000000000000000000000000000ab301b0165004d00200043006c00690065006e0074002e006c006e006b0000001c00120000002b00efbe2b82f657263bda011c00220000001d00efbe02004d00610069006c0043006c00690065006e00740000001c00220000001e00efbe02005500730065007200500069006e006e006500640000001c00000000820100003a001f80c827341f105c1042aa032ee45287d668260001002600efbe12000000cf27cefc0a3bda0168fa3d2e0b3bda01d144ae71263bda01140056003100000000009e57ac6d11005461736b42617200400009000400efbe9e57c9539e57ac6d2e000000c2c001000000010000000000000000000000000000000d7e27005400610073006b0042006100720000001600f0003200800500009e570a55200056495355414c7e312e4c4e4b00005e0009000400efbe9e5733569e57ae6d2e00000013b401000000040000000000000000000000000000009b443600560069007300750061006c002000530074007500640069006f00200043006f00640065002e006c006e006b0000001c00120000002b00efbed144ae71263bda011c00420000001d00efbe02004d006900630072006f0073006f00660074002e00560069007300750061006c00530074007500640069006f0043006f006400650000001c00220000001e00efbe02005500730065007200500069006e006e006500640000001c000000ff" }
@{path = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Taskband"; name = "FavoritesResolve"; propertyType = "Binary"; data = "640600004c0000000114020000000000c0000000000000468100800000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000120614001f809bd434424502f34db7803893943456e1fc050000fa0441505053e80408000300000000000000780200003153505355284c9f799f394ba8d0e1d42de1d5f36100000011000000001f000000280000004d006900630072006f0073006f00660074002e00570069006e0064006f00770073005400650072006d0069006e0061006c005f003800770065006b0079006200330064003800620062007700650000001100000027000000000b000000ffff0000110000000e00000000130000000200000011000000190000000013000000010000008500000015000000001f000000390000004d006900630072006f0073006f00660074002e00570069006e0064006f00770073005400650072006d0069006e0061006c005f0031002e00310038002e0033003100380031002e0030005f007800360034005f005f003800770065006b00790062003300640038006200620077006500000000006900000005000000001f0000002c0000004d006900630072006f0073006f00660074002e00570069006e0064006f00770073005400650072006d0069006e0061006c005f003800770065006b0079006200330064003800620062007700650021004100700070000000bd0000000f000000001f0000005600000043003a005c00500072006f006700720061006d002000460069006c00650073005c00570069006e0064006f007700730041007000700073005c004d006900630072006f0073006f00660074002e00570069006e0064006f00770073005400650072006d0069006e0061006c005f0031002e00310038002e0033003100380031002e0030005f007800360034005f005f003800770065006b0079006200330064003800620062007700650000001d000000200000000048000000e55f9bd0c945b542a874c452684c8cb400000000cd010000315350534d0bd48669903c44819a2a54090dccec4d0000000c000000001f0000001d00000049006d0061006700650073005c0053007100750061007200650031003500300078003100350030004c006f0067006f002e0070006e006700000000004900000002000000001f0000001b00000049006d0061006700650073005c00530071007500610072006500340034007800340034004c006f0067006f002e0070006e00670000000000490000000d000000001f0000001b00000049006d0061006700650073005c00570069006400650033003100300078003100350030004c006f0067006f002e0070006e00670000000000110000000400000000130000000078d4ff11000000050000000013000000ffffffff3d00000013000000001f0000001500000049006d0061006700650073005c004c006100720067006500540069006c0065002e0070006e00670000000000110000000e0000000013000000a1040000250000000b000000001f000000090000005400650072006d0069006e0061006c00000000003d00000014000000001f0000001500000049006d0061006700650073005c0053006d0061006c006c00540069006c0065002e0070006e00670000000000000000003100000031535053b1166d44ad8d7048a748402ea43d788c15000000640000000015000000f70400000000000000000000410000003153505330f125b7ef471a10a5f102608c9eebac250000000a000000001f000000090000005400650072006d0069006e0061006c0000000000000000002d00000031535053b377ed0d14c66c45ae5b285b38d7b01b110000000700000000130000000000000000000000000000000000640000001d00efbe02004d006900630072006f0073006f00660074002e00570069006e0064006f00770073005400650072006d0069006e0061006c005f003800770065006b00790062003300640038006200620077006500210041007000700000000005220000001e00efbe02005500730065007200500069006e006e006500640000000005120000002b00efbe11da88d7203bda010005640000001d00efbe02004d006900630072006f0073006f00660074002e00570069006e0064006f00770073005400650072006d0069006e0061006c005f003800770065006b00790062003300640038006200620077006500210041007000700000000005000000000000080300004c0000000114020000000000c00000000000004683008000200000004645b2309a5cd9012b08b3309a5cd90166bab2309a5cd90179050000000000000100000000000000000000000000000076013a001f80c827341f105c1042aa032ee45287d668260001002600efbe12000000053783fdb757d90127e55b24b857d9010bd0b1309a5cd901140056003100000000007556d4a411005461736b42617200400009000400efbe70563d1c7656cc3d2e000000498501000000010000000000000000000000000000001715e0005400610073006b0042006100720000001600e4003200790500007656454520004449524543547e312e4c4e4b0000560009000400efbe76564545765645452e000000e501060000002a0000000000000000000000000000009ae23e004400690072006500630074006f007200790020004f007000750073002e006c006e006b0000001c00220000001e00efbe02005500730065007200500069006e006e006500640000001c00120000002b00efbe2b08b3309a5cd9011c003e0000001d00efbe02004700500053006f006600740077006100720065002e004400690072006500630074006f00720079004f0070007500730000001c0000009b0000001c000000010000001c0000002d000000000000009a0000001100000003000000f3e6ee6a1000000000433a5c55736572735c6261735c417070446174615c526f616d696e675c4d6963726f736f66745c496e7465726e6574204578706c6f7265725c517569636b204c61756e63685c557365722050696e6e65645c5461736b4261725c4469726563746f7279204f7075732e6c6e6b000060000000030000a058000000000000006c6170746f70000000000000000000003c0f4e300424bb478d1c94f009ee61d308cdd1081dc8ed11bc204c796ed737593c0f4e300424bb478d1c94f009ee61d308cdd1081dc8ed11bc204c796ed7375945000000090000a03900000031535053b1166d44ad8d7048a748402ea43d788c1d000000680000000048000000d15c644bd685944f94138d6eb0ee6668000000000000000000000000e40200004c0000000114020000000000c000000000000046830080002000000011783fd21b3bda01214f580c213bda0107d839f20a3bda0186090000000000000100000000000000000000000000000052013a001f80c827341f105c1042aa032ee45287d668260001002600efbe12000000cf27cefc0a3bda0168fa3d2e0b3bda01214f580c213bda01140056003100000000009e57d36811005461736b42617200400009000400efbe9e57c9539e57d3682e000000c2c00100000001000000000000000000000000000000fad900015400610073006b0042006100720000001600c0003200860900009e57935320004d4943524f537e312e4c4e4b0000560009000400efbe9e57ac639e57d9682e000000dc50020000000a000000000000000000000000000000f9762a014d006900630072006f0073006f0066007400200045006400670065002e006c006e006b0000001c00220000001e00efbe02005500730065007200500069006e006e006500640000001c00120000002b00efbe214f580c213bda011c001a0000001d00efbe02004d005300450064006700650000001c0000009b0000001c000000010000001c0000002d000000000000009a00000011000000030000000071aad41000000000433a5c55736572735c6261735c417070446174615c526f616d696e675c4d6963726f736f66745c496e7465726e6574204578706c6f7265725c517569636b204c61756e63685c557365722050696e6e65645c5461736b4261725c4d6963726f736f667420456467652e6c6e6b000060000000030000a058000000000000006c6170746f7000000000000000000000c08e6f6ba16fa64d8494a43bb12427acf8f035630da7ee11a7e6d0374504eef3c08e6f6ba16fa64d8494a43bb12427acf8f035630da7ee11a7e6d0374504eef345000000090000a03900000031535053b1166d44ad8d7048a748402ea43d788c1d0000006800000000480000006714ec02933fe84687afa5163d9e9559000000000000000000000000dd0200004c0000000114020000000000c0000000000000468300800020000000ada022f11f3bda012b82f657263bda0155c677aa0d3bda0150040000000000000100000000000000000000000000000050013a001f80c827341f105c1042aa032ee45287d668260001002600efbe12000000cf27cefc0a3bda0168fa3d2e0b3bda012b82f657263bda01140056003100000000009e57936d11005461736b42617200400009000400efbe9e57c9539e57936d2e000000c2c00100000001000000000000000000000000000000a6be39005400610073006b0042006100720000001600be003200500400009e5703562000454d434c49457e312e4c4e4b00004c0009000400efbe9e575b679e57966d2e000000ad520200000007000000000000000000000000000000ab301b0165004d00200043006c00690065006e0074002e006c006e006b0000001c00220000001e00efbe02005500730065007200500069006e006e006500640000001c00120000002b00efbe2b82f657263bda011c00220000001d00efbe02004d00610069006c0043006c00690065006e00740000001c000000960000001c000000010000001c0000002d000000000000009500000011000000030000000071aad41000000000433a5c55736572735c6261735c417070446174615c526f616d696e675c4d6963726f736f66745c496e7465726e6574204578706c6f7265725c517569636b204c61756e63685c557365722050696e6e65645c5461736b4261725c654d20436c69656e742e6c6e6b000060000000030000a058000000000000006c6170746f7000000000000000000000c08e6f6ba16fa64d8494a43bb12427ace3f735630da7ee11a7e6d0374504eef3c08e6f6ba16fa64d8494a43bb12427ace3f735630da7ee11a7e6d0374504eef345000000090000a03900000031535053b1166d44ad8d7048a748402ea43d788c1d0000006800000000480000006714ec02933fe84687afa5163d9e9559000000000000000000000000180300004c0000000114020000000000c0000000000000468300800020000000511c63e10d3bda019fa9b071263bda0165bd9a950c3bda0180050000000000000100000000000000000000000000000082013a001f80c827341f105c1042aa032ee45287d668260001002600efbe12000000cf27cefc0a3bda0168fa3d2e0b3bda01d144ae71263bda01140056003100000000009e57ac6d11005461736b42617200400009000400efbe9e57c9539e57ac6d2e000000c2c001000000010000000000000000000000000000000d7e27005400610073006b0042006100720000001600f0003200800500009e570a55200056495355414c7e312e4c4e4b00005e0009000400efbe9e5733569e57ae6d2e00000013b401000000040000000000000000000000000000009b443600560069007300750061006c002000530074007500640069006f00200043006f00640065002e006c006e006b0000001c00220000001e00efbe02005500730065007200500069006e006e006500640000001c00120000002b00efbed144ae71263bda011c00420000001d00efbe02004d006900630072006f0073006f00660074002e00560069007300750061006c00530074007500640069006f0043006f006400650000001c0000009f0000001c000000010000001c0000002d000000000000009e00000011000000030000000071aad41000000000433a5c55736572735c6261735c417070446174615c526f616d696e675c4d6963726f736f66745c496e7465726e6574204578706c6f7265725c517569636b204c61756e63685c557365722050696e6e65645c5461736b4261725c56697375616c2053747564696f20436f64652e6c6e6b000060000000030000a058000000000000006c6170746f7000000000000000000000c08e6f6ba16fa64d8494a43bb12427ac02f835630da7ee11a7e6d0374504eef3c08e6f6ba16fa64d8494a43bb12427ac02f835630da7ee11a7e6d0374504eef345000000090000a03900000031535053b1166d44ad8d7048a748402ea43d788c1d0000006800000000480000006714ec02933fe84687afa5163d9e9559000000000000000000000000" }
@{path = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Taskband"; name = "FavoritesChanges"; propertyType = "DWord"; data = 0x0D }
@{path = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Taskband"; name = "FavoritesVersion"; propertyType = "DWord"; data = 0x03 }
)
static [hashtable[]] $pinnedItems = @(
@{name = "Microsoft Edge"; path = "$env:AppData\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\Microsoft Edge.lnk"; data = "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" }
@{name = "Directory Opus"; path = "$env:AppData\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\Directory Opus.lnk"; data = "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" }
@{name = "eM Client"; path = "$env:AppData\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\eM Client.lnk"; data = "TAAAAAEUAgAAAAAAwAAAAAAAAEabAAAAIAAAAACie8eJ+9kBwGumqA072gEAonvHifvZAVAGBAAAAAAAAQAAAAAAAAAAAAAAAAAAAI0BFAAfUOBP0CDqOmkQotgIACswMJ0ZAC9DOlwAAAAAAAAAAAAAAAAAAAAAAAAAmAAxAAAAAACeVwFWEQBQUk9HUkF+MgAAgAAJAAQA776nVBornlcBVi4AAAC5BgAAAAABAAAAAAAAAAAAVgAAAAAA8NHDAFAAcgBvAGcAcgBhAG0AIABGAGkAbABlAHMAIAAoAHgAOAA2ACkAAABAAHMAaABlAGwAbAAzADIALgBkAGwAbAAsAC0AMgAxADgAMQA3AAAAGABcADEAAAAAAJ5XA1YQAEVNQ0xJRX4xAABEAAkABADvvp5XAVaeVwNWLgAAAKURBAAAAAEAAAAAAAAAAAAAAAAAAAAtxCEBZQBNACAAQwBsAGkAZQBuAHQAAAAYAGoAMgBQBgQASlfodiAATUFJTENMfjEuRVhFAABOAAkABADvvkpX6HaeVwFWLgAAAPuvAgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAATQBhAGkAbABDAGwAaQBlAG4AdAAuAGUAeABlAAAAHAAAAF4AAAAcAAAAAQAAABwAAAAtAAAAAAAAAF0AAAARAAAAAwAAAABxqtQQAAAAAEM6XFByb2dyYW0gRmlsZXMgKHg4NilcZU0gQ2xpZW50XE1haWxDbGllbnQuZXhlAAA7AC4ALgBcAC4ALgBcAC4ALgBcAC4ALgBcAC4ALgBcAFAAcgBvAGcAcgBhAG0AIABGAGkAbABlAHMAIAAoAHgAOAA2ACkAXABlAE0AIABDAGwAaQBlAG4AdABcAE0AYQBpAGwAQwBsAGkAZQBuAHQALgBlAHgAZQAhAEMAOgBcAFAAcgBvAGcAcgBhAG0AIABGAGkAbABlAHMAIAAoAHgAOAA2ACkAXABlAE0AIABDAGwAaQBlAG4AdABcABAAAAAFAACgKgAAAMUAAAAcAAAACwAAoO9AWnz7oPxLh0rA8uC5+o7FAAAAYAAAAAMAAKBYAAAAAAAAAGRlc2t0b3AtMXQ2NzljMwDAjm9roW+mTYSUpDuxJCesZCschEmn7hGn49A3RQTu88COb2uhb6ZNhJSkO7EkJ6xkKxyESafuEafj0DdFBO7zywAAAAkAAKBBAAAAMVNQU+KKWEa8TDhDu/wTkyaYbc4lAAAABAAAAAAfAAAACQAAAFMALQAxAC0ANQAtADEAOAAAAAAAAAAAAEUAAAAxU1BTVShMn3mfOUuo0OHULeHV8ykAAAAFAAAAAB8AAAALAAAATQBhAGkAbABDAGwAaQBlAG4AdAAAAAAAAAAAADkAAAAxU1BTsRZtRK2NcEinSEAupD14jB0AAABoAAAAAEgAAABnFOwCkz/oRoevpRY9npVZAAAAAAAAAAAAAAAA" }
@{name = "Visual Studio Code"; path = "$env:AppData\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\Visual Studio Code.lnk"; data = "TAAAAAEUAgAAAAAAwAAAAAAAAEabAAAAIAAAABKnspAMO9oBNibBkgw72gEAALRlpS3aAbB/OAkAAAAAAQAAAAAAAAAAAAAAAAAAAC4COgAfSUcaA1lyP6dEicVVlf5rMO4mAAEAJgDvvhAAAABWLuj7CjvaAbEnsigMO9oBFfmKlQw72gEUAIIAdAAcAENGU0YWADEAAAAAAJ5Xm1MSAEFwcERhdGEAAAB0Gllelt/TSI1nFzO87ii6xc36359nVkGJR8XHa8C2f0AACQAEAO++nlebU55X+FQuAAAABLkBAAAAAgAAAAAAAAAAAAAAAAAAAM4p9wBBAHAAcABEAGEAdABhAAAAQgBQADEAAAAAAJ5XBFUQAExvY2FsADwACQAEAO++nlebU55XBFUuAAAAGLkBAAAAAgAAAAAAAAAAAAAAAAAAAGS0NQBMAG8AYwBhAGwAAAAUAFoAMQAAAAAAnlcGVRAAUHJvZ3JhbXMAAEIACQAEAO++nlfvVJ5XBlUuAAAAXQEAAAAAAgAAAAAAAAAAAAAAAAAAAAWhYgBQAHIAbwBnAHIAYQBtAHMAAAAYAGwAMQAAAAAAnlcJVTAATUlDUk9TfjEAAFQACQAEAO++nlcGVZ5XClUuAAAAVAMCAAAAAQAAAAAANAEAAAAAAAAAAEy+vQBNAGkAYwByAG8AcwBvAGYAdAAgAFYAUwAgAEMAbwBkAGUAAAAYAFoAMgCwfzgJjVdcSiAAQ29kZS5leGUAAEIACQAEAO++nlcGVZ5XCFUuAAAAWgMCAAAAAQAAAAAABAIAAAAAAAAAAAAAAABDAG8AZABlAC4AZQB4AGUAAAAYAAAAbQAAABwAAAABAAAAHAAAAC0AAAAAAAAAbAAAABEAAAADAAAAAHGq1BAAAAAAQzpcVXNlcnNcYmFzXEFwcERhdGFcTG9jYWxcUHJvZ3JhbXNcTWljcm9zb2Z0IFZTIENvZGVcQ29kZS5leGUAADsALgAuAFwALgAuAFwALgAuAFwALgAuAFwALgAuAFwALgAuAFwATABvAGMAYQBsAFwAUAByAG8AZwByAGEAbQBzAFwATQBpAGMAcgBvAHMAbwBmAHQAIABWAFMAIABDAG8AZABlAFwAQwBvAGQAZQAuAGUAeABlADUAQwA6AFwAVQBzAGUAcgBzAFwAYgBhAHMAXABBAHAAcABEAGEAdABhAFwATABvAGMAYQBsAFwAUAByAG8AZwByAGEAbQBzAFwATQBpAGMAcgBvAHMAbwBmAHQAIABWAFMAIABDAG8AZABlABwAAAALAACgfA/O8wFJzEqGSNXUSwTvjzoAAABgAAAAAwAAoFgAAAAAAAAAZGVza3RvcC0xdDY3OWMzAMCOb2uhb6ZNhJSkO7EkJ6wTJByESafuEafj0DdFBO7zwI5va6Fvpk2ElKQ7sSQnrBMkHIRJp+4Rp+PQN0UE7vMzAQAACQAAoIkAAAAxU1BT4opYRrxMOEO7/BOTJphtzm0AAAAEAAAAAB8AAAAuAAAAUwAtADEALQA1AC0AMgAxAC0AMgA0ADQANAA1ADYAMgA5ADAAMgAtADIAMAA3ADIAMQA1ADIAMgAzAC0ANAAxADMAOAAzADIANgAzADAAMgAtADEAMAAwADAAAAAAAAAAZQAAADFTUFNVKEyfeZ85S6jQ4dQt4dXzSQAAAAUAAAAACAAAADYAAABNAGkAYwByAG8AcwBvAGYAdAAuAFYAaQBzAHUAYQBsAFMAdAB1AGQAaQBvAEMAbwBkAGUAAAAAAAAAAAA5AAAAMVNQU7EWbUStjXBIp0hALqQ9eIwdAAAAaAAAAABIAAAAZxTsApM/6EaHr6UWPZ6VWQAAAAAAAAAAAAAAAA==" }
)
static [void] RestoreFiles() {
foreach ($pinnedItem in [Taskbar]::pinnedItems) {
$filePath = [Utils]::WriteFile($pinnedItem.data, $pinnedItem.path)
Write-Host "[OK] Created '$($pinnedItem.name)' link" -ForegroundColor Green
}
}
}
class DesktopIcons {
static [hashtable[]] $registry = @(
@{path = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel"; displayName = "My Computer"; name = '{20D04FE0-3AEA-1069-A2D8-08002B30309D}'; propertyType = "DWord"; data = 0 }
@{path = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel"; displayName = "Recycle bin"; name = '{645FF040-5081-101B-9F08-00AA002F954E}'; propertyType = "DWord"; data = 1 }
@{path = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel"; displayName = "User's Files"; name = '{59031A47-3F72-44A7-89C5-5595FE6B30EE}'; propertyType = "DWord"; data = 1 }
@{path = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel"; displayName = "OneDrive"; name = '{018D5C66-4533-4307-9B53-224DE2ED1FE6}'; propertyType = "DWord"; data = 1 }
)
static [void] RestoreDesktopIcons() {
<#
.SYNOPSIS
Restores desktop icons by removing all shortcut files from the Public and User desktops and restoring the registry settings.
.DESCRIPTION
This function removes all shortcut files from the Public and User desktops and then calls the RestoreRegistry method of the Utils class to restore the registry settings for desktop icons.
.PARAMETER None
This function does not accept any parameters.
.EXAMPLE
RestoreDesktopIcons
This example demonstrates how to use the RestoreDesktopIcons function to restore desktop icons.
#>
Write-Host "[Info] Removing desktop icons..." -ForegroundColor Blue
Remove-Item "$env:Public\Desktop\*.lnk" -ErrorAction SilentlyContinue
Remove-Item "$env:UserProfile\Desktop\*.lnk" -ErrorAction SilentlyContinue
[Utils]::RestoreRegistry([DesktopIcons]::registry)
}
}
<Configuration>
<vGPU>Enable</vGPU>
<Networking>Default</Networking>
<AudioInput>Disable</AudioInput>
<VideoInput>Disable</VideoInput>
<PrinterRedirection>Disable</PrinterRedirection>
<ClipboardRedirection>Default</ClipboardRedirection>
<ProtectedClient>Enable</ProtectedClient>
<MappedFolders>
<MappedFolder>
<HostFolder>C:\Users\bas\Downloads\.sandbox</HostFolder>
<SandboxFolder>C:\Users\WDAGUtilityAccount\Downloads</SandboxFolder>
<ReadOnly>False</ReadOnly>
</MappedFolder>
</MappedFolders>
<LogonCommand>
<Command>powershell -executionpolicy Unrestricted -Command "start powershell { -Command \"Set-ExecutionPolicy Unrestricted -Scope LocalMachine -Force; Invoke-WebRequest -UseBasicParsing -Uri 'https://gist.githubusercontent.com/QNimbus/83a86ee072d2b4dbfd13f2fe901796df/raw/scripts---bootstrap_sandbox.ps1' | Select-Object -ExpandProperty Content | Invoke-Expression \"}"</Command>
</LogonCommand>
</Configuration>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment