Move scripts properly

This commit is contained in:
Manuel Thalmann 2024-08-07 21:05:32 +02:00
parent 36336e332d
commit 2dfb9d58bf
66 changed files with 75 additions and 76 deletions

View file

@ -0,0 +1,134 @@
#!/bin/bash
. "$PSScriptRoot/Context.ps1";
$null = New-Module {
. "$PSScriptRoot/System.ps1";
<#
.SYNOPSIS
Runs an action involving browser automation.
.PARAMETER Action
The action to execute.
#>
function Start-BrowserAutomation {
param(
[scriptblock] $Action
)
if (-not ("OpenQA.Selenium.Firefox.FirefoxDriver" -as [type])) {
$zipFile = [System.IO.Compression.ZipFile]::OpenRead((Get-Package Selenium.WebDriver).Source);
$webDriver = ($zipFile.Entries | Where-Object { $_.FullName -like "lib/net6.0/WebDriver.dll" })[0];
$stream = [System.IO.MemoryStream]::new();
$reader = [System.IO.StreamReader]($webDriver).Open();
$reader.BaseStream.CopyTo($stream);
[byte[]]$bytes = $stream.ToArray();
$reader.Close();
$reader.Dispose();
$stream.Close();
$stream.Dispose();
$zipFile.Dispose();
$null = [System.Reflection.Assembly]::Load($bytes);
}
& $Action;
}
<#
.SYNOPSIS
Downloads a file from the specified url using browser automation.
.PARAMETER URL
The url to download the file from.
.PARAMETER Action
The action to execute in the browser for initiating the download.
.PARAMETER OutDir
The directory to download the file to.
#>
function Start-CustomBrowserDownload {
param(
[string] $URL,
[scriptblock] $Action,
[string] $OutDir
)
if (-not $OutDir) {
$OutDir = ".";
}
$dir = New-TemporaryDirectory;
Write-Host "Downloading ``$URL`` using browser automation…";
$options = [OpenQA.Selenium.Firefox.FirefoxOptions]::new();
$options.SetPreference("browser.download.folderList", 2);
$options.SetPreference("browser.download.dir", "$dir");
$downloadChecker = {
$files = Get-ChildItem $dir;
if ((@($files)).Count -gt 0) {
foreach ($file in $files) {
try {
$stream = [System.IO.File]::Open($file.FullName, [System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None);
if ($stream) {
$stream.Close();
}
}
catch {
return $true;
}
}
return $false;
} else {
return $true;
}
};
$browser = [OpenQA.Selenium.Firefox.FirefoxDriver]::new($options);
$browser.Navigate().GoToUrl($URL);
& $Action -Browser $browser;
while (& $downloadChecker) {
Write-Host "Waiting for the download to finish…";
Start-Sleep 1;
}
$file = Get-ChildItem $dir;
$result = Move-Item $file $OutDir -PassThru;
$browser.Quit();
Remove-Item -Recurse $dir;
$result;
}
<#
.SYNOPSIS
Downloads a file from the specified url using browser automation.
.PARAMETER URL
The url to download the file from.
.PARAMETER ButtonSelector
The jQuery selector for finding the download button.
.PARAMETER OutDir
The directory to download the file to.
#>
function Start-BrowserDownload {
param(
[string] $URL,
[string] $ButtonSelector,
[string] $OutDir = $null
)
Start-CustomBrowserDownload @PSBoundParameters -Action {
param(
[OpenQA.Selenium.Firefox.FirefoxDriver] $Browser
)
$Browser.FindElement([OpenQA.Selenium.By]::CssSelector($ButtonSelector)).Click();
};
}
};

View file

@ -0,0 +1,233 @@
using namespace Microsoft.Win32;
using namespace System.Security.AccessControl;
using namespace System.Security.Principal;
enum SetupStage {
Initialize
Configure
Install
}
$null = New-Module {
[string] $configRoot = "HKLM:\Software\PortValhalla";
[string] $stageOption = "Stage";
[string] $finishedOption = "Finished";
[RegistryKey] $key = $null;
<#
.SYNOPSIS
Converts the specified path to linux and escapes it for the use in a script.
.PARAMETER Path
The path to convert.
#>
function ConvertTo-LinuxPath {
param(
[string] $Path
)
$job = Start-Job {
$env:Value = Resolve-Path $Using:Path;
$env:WSLENV = "Value/p";
$result = wsl -- bash -c 'echo "$Value"';
wsl -e printf "%q" "$result";
};
Receive-Job -Wait $job;
}
<#
.SYNOPSIS
Gets the registry key containing options related to the setup.
#>
function Get-SetupConfigKey {
if (-not (Test-Path $configRoot)) {
$key = New-Item $configRoot;
$acl = Get-Acl $configRoot;
$acl.AddAccessRule(
[RegistryAccessRule]::new(
[SecurityIdentifier]::new([WellKnownSidType]::BuiltinUsersSid, $null),
[RegistryRights]::FullControl,
[InheritanceFlags]::ObjectInherit -bor [InheritanceFlags]::ContainerInherit,
[PropagationFlags]::None,
[AccessControlType]::Allow));
Set-Acl $configRoot $acl;
} else {
$key = Get-Item $configRoot;
}
return $key;
}
<#
.SYNOPSIS
Runs a script based on the `config.fish` script.
.PARAMETER Script
The script to run.
#>
function Invoke-ConfigScript {
param(
[string] $Script
)
$scriptPath = "$PSScriptRoot/../../Common/Scripts/config.fish";
function fish {
wsl --shell-type login -- nix --extra-experimental-features "nix-command flakes" run nixpkgs`#fish -- $args
}
fish -c ". $(ConvertTo-LinuxPath $scriptPath); $Script" | ConvertFrom-Json;
}
<#
.SYNOPSIS
Gets a configuration option.
.PARAMETER Name
The name of the option to get.
#>
function Get-Config {
param(
[string] $Name
)
Invoke-ConfigScript "getConfig $Name --json";
}
<#
.SYNOPSIS
Gets the names of the users to create.
#>
function Get-Users {
Invoke-ConfigScript "getUsers";
}
<#
.SYNOPSIS
Gets the value of an option related to the setup.
.PARAMETER Name
The name of the option value to get.
#>
function Get-SetupOption {
param(
[string] $Name
)
$key = Get-SetupConfigKey;
if ($key.GetValueNames().Contains($Name)) {
return $key.GetValue($Name);
} else {
return $null;
}
}
<#
.SYNOPSIS
Sets the value of an option related to the setup.
.PARAMETER Name
The name of the option to set.
.PARAMETER Value
The value to set the option to.
#>
function Set-SetupOption {
param(
[string] $Name,
$Value
)
$key = Get-SetupConfigKey;
$null = Set-ItemProperty ($key.PSPath) -Name $Name -Value $Value;
}
<#
.SYNOPSIS
Gets the name of the current setup stage.
#>
function Get-Stage {
$stage = Get-SetupOption $stageOption;
if ($null -ne $stage) {
$stage = [SetupStage]$stage;
}
return $stage;
}
<#
.SYNOPSIS
Sets the current stage.
.PARAMETER Name
The name to set the current stage to.
#>
function Set-Stage {
param(
$Name
)
if (-not (($null -eq $Name) -or ($Name -is [string]))) {
$Name = ([SetupStage]$Name).ToString();
}
$null = Set-SetupOption $stageOption $Name;
}
<#
.SYNOPSIS
Gets a value indicating whether the setup has finished.
#>
function Get-IsFinished {
return [bool] (Get-SetupOption $finishedOption);
}
<#
.SYNOPSIS
Sets a value indicating whether the setup has finished.
#>
function Set-IsFinished {
param(
$Value
)
Set-SetupOption $finishedOption $true;
}
<#
.SYNOPSIS
Checks whether the specified software collection is enabled.
.PARAMETER Name
The name of the collection to check.
#>
function Test-Collection {
param(
[string] $Name
)
Get-Config "valhalla.software.$Name";
}
<#
.SYNOPSIS
Checks whether the current user is the setup user.
#>
function Test-SetupUser {
$env:UserName -eq (Get-Config "valhalla.windows.setupUser");
}
<#
.SYNOPSIS
Checks whether the active session is executed with admin rights.
#>
function Test-Admin {
net session 2> $null | Out-Null;
return $?;
}
}

View file

@ -0,0 +1,417 @@
#!/bin/pwsh
. "$PSScriptRoot/../../Common/Scripts/Entrypoints.ps1";
. "$PSScriptRoot/../../Common/Software/PowerShell/profile.ps1";
class Context {
[string]$EntryPoint;
[string]$RootDir;
[string]$BackupName;
[string[]]$UserNames;
[string]$AdminName = "Admin";
[string]$ConfigRoot = "HKLM:\Software\PortValhalla";
[string]$RunOnceName = "PortValhalla";
[string]$StagePropertyName = "Stage";
Context() {
try {
$this.EntryPoint = Get-Entrypoint;
}
catch { }
}
[string] ProjectRoot() {
return Resolve-Path (Join-Path $PSScriptRoot ".." ".." "..");
}
[string] BackupRoot() {
if (-not $this.RootDir)
{
return Join-Path $this.ProjectRoot() "backup" $this.BackupName;
}
else
{
return $this.RootDir;
}
}
[void] RemoveDesktopIcon($pattern) {
Remove-Item "$env:PUBLIC/Desktop/$pattern";
Remove-Item "~/Desktop/$pattern";
}
[void] RemoveTaskbarItem($pattern) {
Import-Module -UseWindowsPowerShell PinnedItem;
Get-PinnedItem -Type TaskBar | Where-Object { $_.Name -like "$pattern" } | Remove-PinnedItem;
}
[void] AddPowerShellProfileStatement([string] $statement) {
$this.AddPowerShellProfileStatement($true, $statement);
}
[void] AddPowerShellProfileStatement([string] $category, [string] $statement) {
$this.AddPowerShellProfileStatement($true, $category, $statement);
}
[void] AddPowerShellProfileStatement([bool] $system, [string] $statement) {
$this.AddPowerShellProfileStatement($system, $null, $statement);
}
[void] AddPowerShellProfileStatement([bool] $system, [string] $category, [string] $statement) {
if ($category) {
$overwrite = $true;
} else {
$overwrite = $false;
}
$this.AddPowerShellProfileStatement($system, $category, $statement, $overwrite);
}
[void] AddPowerShellProfileStatement([bool] $system, [string] $category, [string] $statement, [bool] $overwrite) {
if ($system) {
Add-PowerShellProfileStatement -System -Category $category -Statement $statement -Overwrite $overwrite;
} else {
Add-PowerShellProfileStatement -Category $category -Statement $statement -Overwrite $overwrite;
}
}
[Microsoft.Win32.RegistryKey] EnsureConfigKey() {
if (-not (Test-Path $this.ConfigRoot)) {
$null = New-Item $this.ConfigRoot;
$acl = Get-Acl $this.ConfigRoot;
$acl.AddAccessRule(
[System.Security.AccessControl.RegistryAccessRule]::new(
[System.Security.Principal.SecurityIdentifier]::new([System.Security.Principal.WellKnownSidType]::BuiltinUsersSid, $null),
[System.Security.AccessControl.RegistryRights]::FullControl,
[System.Security.AccessControl.InheritanceFlags]::ObjectInherit -bor [System.Security.AccessControl.InheritanceFlags]::ContainerInherit,
[System.Security.AccessControl.PropagationFlags]::None,
[System.Security.AccessControl.AccessControlType]::Allow));
Set-Acl $this.ConfigRoot $acl;
}
return Get-Item $this.ConfigRoot;
}
[object] Get([string] $key) {
$configKey = $this.EnsureConfigKey();
if ($configKey.GetValueNames().Contains($key)) {
return $configKey.GetValue($key);
} else {
return $null;
}
}
[void] Set([string] $key, $value) {
$this.Set($key, $value, "ExpandString");
}
[void] Set([string] $key, $value, [Microsoft.Win32.RegistryValueKind] $type) {
$configKey = $this.EnsureConfigKey();
$null = Set-ItemProperty -Path $configKey.PSPath -Name $key -Value $value -Type $type;
}
[void] Remove([string] $key) {
$configKey = $this.EnsureConfigKey();
$null = Remove-ItemProperty -Path $configKey.PSPath -Name $key;
}
[void] SetStage([string] $name) {
$this.Set($this.StagePropertyName, $name);
}
[string] GetStage() {
return $this.Get($this.StagePropertyName);
}
[void] RemoveStage() {
$this.Remove($this.StagePropertyName);
}
[string] ArchivePath($name) {
return Join-Path $this.BackupRoot() "$name.7z";
}
[string] FileArchivePath($name) {
return $this.ArchivePath($(Join-Path "Files" $name));
}
[string] SoftwareArchive([string]$softwareName) {
return $this.ArchivePath($softwareName);
}
[void] Backup([string]$sourcePath, [string]$archivePath) {
$this.Backup($sourcePath, $archivePath, @());
}
[void] Backup([string]$sourcePath, [string]$archivePath, [string[]]$arguments) {
$this.Backup($sourcePath, $archivePath, $arguments, $true);
}
[void] Backup([string]$sourcePath, [string]$archivePath, [string[]]$arguments, [bool]$split) {
if (Test-Path $archivePath) {
Remove-Item -Recurse $archivePath;
}
if (Test-Path "$archivePath.*") {
Remove-Item -Recurse "$archivePath.*";
}
Start-Process -WorkingDirectory "$sourcePath" `
-FilePath "7z" `
-ArgumentList (
@(
"a",
"-xr!desktop.ini",
"-xr!thumbs.db",
"-xr!Thumbs.db",
"-slp",
$archivePath) + $arguments + $(
if ($split) {
@("-v2g");
} else {
@();
}
)) `
-Wait `
-NoNewWindow;
}
[void] Restore([string]$archivePath, [string]$destinationPath) {
$this.Restore($archivePath, $destinationPath, @());
}
[void] Restore([string]$archivePath, [string]$destinationPath, [string[]] $arguments) {
if (-not (Test-Path -PathType Leaf $archivePath)) {
$archivePath = "$archivePath.001";
}
if (-not (Test-Path -PathType Leaf $archivePath)) {
Write-Information (
[string]::Join(
"`n",
@(
"An archive at the specified path $archivePath does not exist.",
"No restoration will be performed.")));
}
else {
if (-not (Test-Path -PathType Container $destinationPath)) {
New-Item -ItemType Directory "$destinationPath";
}
Start-Process -WorkingDirectory "$destinationPath" `
-FilePath "7z" `
-ArgumentList @("x", "$archivePath") + $arguments `
-Wait `
-NoNewWindow;
}
}
[string] GetTempDirectory() {
$tempDir = Join-Path $([System.IO.Path]::GetTempPath()) $([System.IO.Path]::GetRandomFileName());
$null = New-Item -ItemType Directory $tempDir;
return $tempDir;
}
[void] ProcessDefaultUserKey([System.Action[Microsoft.Win32.RegistryKey]] $action) {
$rootPath = "HKLM:\DefaultUser";
$regRootPath = $rootPath.Replace(":", "");
$hivePath = "$env:SystemDrive\Users\Default\NTUSER.dat"
$null = & reg load $regRootPath $hivePath;
$root = Get-Item $rootPath;
$action.Invoke($root);
$root.Handle.Close();
[System.GC]::Collect();
& reg unload $regRootPath;
}
[void] ProcessLogonKey([System.Action[Microsoft.Win32.RegistryKey]] $action) {
$key = Get-Item "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon";
$action.Invoke($key);
}
[Microsoft.Win32.RegistryKey] GetSystemPolicyKey() {
$keyPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System";
return Get-Item "$keyPath";
}
[Microsoft.Win32.RegistryKey] GetRunOnceKey() {
return $this.GetRunOnceKey($null);
}
[Microsoft.Win32.RegistryKey] GetRunOnceKey([Microsoft.Win32.RegistryKey] $userKey) {
if (-not $userKey) {
$userKey = Get-Item "HKCU:\";
}
Push-Location $userKey.PSPath;
$runOncePath = "Software\Microsoft\Windows\CurrentVersion\RunOnce";
if (-not (Test-Path $runOncePath)) {
New-Item $runOncePath;
}
$result = Get-Item $runOncePath;
Pop-Location;
return $result;
}
[bool] GetUACState() {
return [bool](Get-ItemPropertyValue -Path ($this.GetSystemPolicyKey().PSPath) -Name "EnableLUA");
}
[void] SetUACState([bool] $value) {
$null = Set-ItemProperty -Path ($this.GetSystemPolicyKey().PSPath) -Name "EnableLUA" -Value ([int]$value);
}
[void] RegisterReboot() {
$this.RegisterReboot($null);
}
[void] RegisterReboot([Microsoft.Win32.RegistryKey] $userKey) {
$runOnceKey = $this.GetRunOnceKey($userKey);
Set-ItemProperty -Path $runOnceKey.PSPath -Name $this.RunOnceName -Value "pwsh `"$($this.EntryPoint)`"" -Type "ExpandString";
$runOnceKey.Handle.Close();
}
[void] RegisterNewUserReboot() {
$this.ProcessDefaultUserKey({ param ($root) $this.RegisterReboot($root); });
}
[void] DeregisterNewUserReboot() {
$this.ProcessDefaultUserKey({ param ($root) Remove-Item -Path $this.GetRunOnceKey($root).PSPath });
}
[void] SetAutologin($user, $pw) {
$this.ProcessLogonKey(
{
param ($logon)
$path = $logon.PSPath;
Set-ItemProperty $path -Name "AutoAdminLogon" 1;
Set-ItemProperty $path -Name "DefaultUserName" $user;
if ($pw) {
Set-ItemProperty $path -Name "DefaultPassword" $pw;
} else {
Remove-ItemProperty $path -Name "DefaultPassword";
}
});
}
[void] RemoveAutologin() {
$this.ProcessLogonKey(
{
param ($logon)
$path = $logon.PSPath;
Set-ItemProperty $path -Name "AutoAdminLogon" 0;
Remove-ItemProperty $path -Name "DefaultDomainName" -ErrorAction SilentlyContinue;
Remove-ItemProperty $path -Name "DefaultUserName" -ErrorAction SilentlyContinue;
Remove-ItemProperty $path -Name "DefaultPassword" -ErrorAction SilentlyContinue;
});
}
[string] GetNextcloudConfigFile() {
return "$env:APPDATA/Nextcloud/nextcloud.cfg";
}
[void] AddNextcloudSync([string] $localPath, [string] $targetPath) {
$this.AddNextcloudSync($localPath, $targetPath, $false);
}
[void] AddNextcloudSync([string] $localPath, [string] $targetPath, [bool] $virtualFiles) {
Write-Host "Adding a Nextcloud sync";
Write-Information "$targetPath <=> $localPath";
$pattern = "^\d+\\Folders(?:WithPlaceholders)?\\(\d+)";
$folderID = (
Get-Content $($this.GetNextcloudConfigFile()) | `
Where-Object { $_ -match "$pattern" } | `
ForEach-Object { $_ -replace "$pattern.*$","`$1" } | `
Sort-Object -Unique | `
Measure-Object -Maximum).Maximum + 1;
$configName = "Folders";
$localPath = $localPath.Replace("\", "/");
$targetPath = $targetPath.Replace("\", "/");
if ($virtualFiles) {
$configName += "WithPlaceholders";
}
Write-Information "Stopping Nextcloud process";
$nextcloudProcess = Get-Process nextcloud;
$nextcloudPath = [string]$nextcloudProcess[0].Path;
$nextcloudProcess | Stop-Process -Force;
$accountSectionEntered = $false;
$accountSectionLeft = $false;
$newSettings = [string]::Join(
"`n",
@(
"0\$configName\$folderID\localPath=$localPath",
"0\$configName\$folderID\targetPath=$targetPath"));
$oldContent = Get-Content ($this.GetNextcloudConfigFile());
$(
for ($i = 0; $i -lt $oldContent.Count; $i++) {
$line = $oldContent[$i];
if ($line -eq "[Accounts]") {
$accountSectionEntered = $true;
}
if ($line -eq "" -and $accountSectionEntered) {
$accountSectionLeft = $true;
$newSettings;
}
$line;
if (
(-not $accountSectionLeft) -and
($i -eq ($oldContent.Count - 1)))
{
$newSettings;
}
}) | Set-Content ($this.GetNextcloudConfigFile());
Write-Information "New nextcloud config:";
Write-Information (Get-Content $($this.GetNextcloudConfigFile()) | Out-String);
Write-Information "Restarting Nextcloud";
Start-Process $nextcloudPath;
}
[void] Reboot() {
Write-Host "Restarting Computer...";
$this.RegisterReboot();
Restart-Computer -Force;
exit;
}
[void] PreventSleepMode() {
$performanceScheme = "8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c";
$currentScheme = [regex]::Match((powercfg /GETACTIVESCHEME), "Power Scheme GUID: ([0-9a-f-]+) ").Groups[1].Value;
if ($currentScheme -ne $performanceScheme) {
Write-Information "Disabling Power Save mode";
$this.Set("Power Scheme", $currentScheme);
powercfg /S 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c;
}
}
[void] Cleanup() {
$this.DeregisterNewUserReboot();
$this.RemoveAutologin();
$this.SetUACState($true);
$originalScheme = $this.Get("Power Scheme");
Remove-Item $($this.EnsureConfigKey().PSPath);
if ($originalScheme) {
Write-Information "Reset power plan to original state";
powercfg /S $originalScheme;
}
}
}

View file

@ -0,0 +1,14 @@
function Get-Entrypoint() {
$trace = Get-PSCallStack
$call = $trace[$trace.Count - 1];
if ($null -ne $call.ScriptName) {
return $call.ScriptName;
} else {
$call = $CallStack[$CallStack.Count - 2];
return $call.ScriptName;
}
throw "No PowerShell entry point script could be found.";
}

View file

@ -0,0 +1,14 @@
function Start-Operation {
param(
[scriptblock] $Action
)
$ErrorActionPreference = 'Inquire';
$env:WSLENV = "CONFIG_MODULE/p";
if ($env:CONFIG_MODULE) {
$env:CONFIG_MODULE = Resolve-Path $env:CONFIG_MODULE;
}
& $Action;
}

View file

@ -0,0 +1,62 @@
<#
.SYNOPSIS
Converts the specified value into a form to safle inject it into a script.
.PARAMETER value
The value to convert for injection.
#>
function ConvertTo-Injection {
param(
$Value
)
"([System.Text.Encoding]::Default.GetString([System.Convert]::FromBase64String('$(
[System.Convert]::ToBase64String([System.Text.Encoding]::Default.GetBytes($value))
)')))"
}
<#
.SYNOPSIS
Writes a PowerShell script to a specified location.
.PARAMETER FileName
The name of the file to write the script to.
.PARAMETER Script
The script to write to the file.
.PARAMETER Replace
A value indicating whether the file should be overwritten if it exists.
.PARAMETER Append
A value indicating whether the content should be appended if the file already exists.
#>
function Write-PSScript {
param(
[string] $FileName,
[string] $Script,
[Parameter(ParameterSetName="Replace")]
[switch] $Replace,
[Parameter(ParameterSetName="Append")]
[switch] $Append
)
Import-Module PSScriptAnalyzer;
$dirName = Split-Path -Parent $FileName;
$content = Invoke-Formatter -ScriptDefinition $Script;
$exists = Test-Path -PathType Leaf $FileName;
if (-not (Test-Path -PathType Container $dirName)) {
$null = New-Item -ItemType Directory $dirName;
}
if ($exists -and ($Append.IsPresent)) {
Add-Content -Force $FileName "`n$content";
} else {
if ((-not $exists) -or $Replace.IsPresent) {
Set-Content -Force $FileName $content;
} else {
Write-Host "The file ``$FileName`` already exists!";
}
}
}

View file

@ -0,0 +1,196 @@
. "$PSScriptRoot/Config.ps1";
. "$PSScriptRoot/Operations.ps1";
. "$PSScriptRoot/System.ps1";
. "$PSScriptRoot/../Types/InstallerAction.ps1";
$null = New-Module {
. "$PSScriptRoot/BrowserAutomation.ps1";
. "$PSScriptRoot/SoftwareManagement.ps1";
. "$PSScriptRoot/../Types/InstallerAction.ps1";
$userArgument = "name";
<#
.SYNOPSIS
Installs the specified packages using chocolatey.
.PARAMETER Names
The names of the packages to install.
#>
function Install-ChocoPackage {
param(
[switch] $Force,
[string[]] $ArgumentList,
[Parameter(Position=0)]
[string] $Name,
[Parameter(ValueFromRemainingArguments = $true)]
[string[]] $AdditionalNames = @()
)
[System.Collections.ArrayList] $Names = @();
$null = $Names.Add($Name);
$Names.AddRange($AdditionalNames);
if (-not ($Force.IsPresent)) {
for ($i = $Names.Count - 1; $i -ge 0; $i--) {
if (Test-ChocoPackage $Names[$i]) {
$Names.RemoveAt($i);
}
}
}
if ($Names.Count -ge 1) {
choco install -y $ArgumentList $Names;
}
}
<#
.SYNOPSIS
Installs the specified packages using `winget`.
.PARAMETER Names
The names of the packages to install.
#>
function Install-WingetPackage {
param(
[switch] $Force,
[Parameter(ValueFromRemainingArguments = $true)]
[string[]] $Names
)
foreach ($name in $Names) {
if ($Force.IsPresent -and -not (Test-WingetPackage $name)) {
winget install --accept-source-agreements --accept-package-agreements -e --id $name;
}
}
}
<#
.SYNOPSIS
Installs a package downloaded from ASUS.
.PARAMETER URL
The URL to download the package from.
#>
function Install-AsusPackage {
param(
[string] $URL
)
$file = "AsusPackage.zip";
$dir = New-TemporaryDirectory;
$unpackDir = New-TemporaryDirectory;
$null = Push-Location $dir;
Invoke-WebRequest $URL -OutFile $file;
Expand-Archive $file $unpackDir;
$null = Pop-Location;
Remove-Item -Recurse $dir;
$null = Start-Process -Wait -WorkingDirectory $unpackDir -FilePath (Join-Path $unpackDir "AsusSetup.exe") -ArgumentList "/S";
Remove-Item -Recurse $unpackDir;
}
<#
.SYNOPSIS
Downloads and installs a package from the AMD website.
.PARAMETER URL
The URL to download the package from.
#>
function Install-AmdPackage {
param(
[string] $URL
)
$dir = New-TemporaryDirectory;
$cookieBannerSelector = "#onetrust-consent-sdk";
$osSelector = "div[id$='oscategory']>div[id$='-0']";
$downloadSelector = "$osSelector div[id$='panel'] .button a";
$file = Start-CustomBrowserDownload @PSBoundParameters -OutDir $dir -Action {
param(
[OpenQA.Selenium.Firefox.FirefoxDriver] $Browser
)
$osContainer = $Browser.FindElement([OpenQA.Selenium.By]::CssSelector($osSelector));
if (-not ([bool] $osContainer.GetAttribute("cmp-expanded"))) {
$osContainer.Click();
}
$download = {
$browser.FindElement([OpenQA.Selenium.By]::CssSelector($downloadSelector)).Click();
};
try {
& $download;
} catch {
$Browser.ExecuteScript("document.querySelector('$cookieBannerSelector').remove()");
& $download;
}
};
Start-Process -Wait -WorkingDirectory $dir -FilePath $file -ArgumentList "/S";
Remove-Item -Recurse $dir;
}
function Start-SoftwareInstaller {
param(
[string] $Name,
[scriptblock] $Installer = { },
[scriptblock] $Configurator = { },
[scriptblock] $UserConfigurator = { },
[InstallerAction] $Action = [InstallerAction]::Install,
[hashtable] $Arguments
)
if (-not $Name) {
$Name = Split-Path -Leaf (Split-Path -Parent ((Get-PSCallStack)[1].ScriptName));
}
Start-Operation {
if ($null -ne $Name) {
$Name = "``$Name``";
} else {
$Name = "unknown software";
}
$installHandler = {
param(
[InstallerAction] $Action,
[hashtable] $Arguments
)
$Arguments ??= @{ };
$argumentList = @{
installer = $installHandler;
arguments = $Arguments;
};
if ($action -eq ([InstallerAction]::Install)) {
Write-Host "Installing $Name";
& $Installer @argumentList;
} elseif ($Action -eq ([InstallerAction]::Configure)) {
Write-Host "Configuring $Name";
& $Configurator @argumentList;
foreach ($user in Get-Users) {
$Arguments.Add($userArgument, $user);
$argumentList.Add("action", [InstallerAction]::ConfigureUser);
& $installHandler @argumentList;
}
} elseif ($Action -eq ([InstallerAction]::ConfigureUser)) {
if ((-not $Arguments.ContainsKey($userArgument)) -or ($null -eq $Arguments[$userArgument])) {
$argumentList.Add($userArgument, ($env:UserName));
}
Write-Host "Configuring $Name for user ``$($Arguments[$userArgument])``";
& $UserConfigurator @argumentList;
}
};
& $installHandler -Action $Action -Arguments $Arguments;
};
}
}

View file

@ -0,0 +1,100 @@
. "$PSScriptRoot/Context.ps1";
function Install-SoftwarePackage([Context] $context, [string] $location, [string[]] $argumentList = @("/S"), [switch]$local) {
[string]$filePath = "";
[string]$tempDir = $null;
if (-not ($local.IsPresent)) {
$tempDir = $context.GetTempDirectory();
Write-Information "Determining the file name of $location";
$fileName = ([uri]$location).Segments[-1];
Write-Information "$fileName";
$filePath = Join-Path $tempDir $fileName;
Write-Information "Downloading setup file from $location";
Invoke-WebRequest $location -OutFile $filePath;
} else {
$filePath = $location;
}
$fileName = [System.IO.Path]::GetFileName($filePath);
Write-Information "Starting installation of $fileName";
Start-Process -Wait -FilePath $filePath -ArgumentList $argumentList;
if ($tempDir) {
Remove-Item -Recurse $tempDir;
}
}
<#
.SYNOPSIS
Checks whether the specified package has been installed using Chocolatey.
.PARAMETER Name
The name of the package to check.
#>
function Test-ChocoPackage {
[OutputType([bool])]
param(
[string] $Name
);
-not [string]::IsNullOrEmpty((choco list --limit-output --exact $name));
}
<#
.SYNOPSIS
Checks whether a `winget` package with the specified id is installed.
.PARAMETER ID
The id of the package to check.
#>
function Test-WingetPackage {
[OutputType([bool])]
param(
[string] $ID
)
-not (& { $null = winget list --accept-source-agreements -e --id $ID; $?; });
}
<#
.SYNOPSIS
Checks whether a command with the specified name exists.
.PARAMETER Name
The name of the command to check.
#>
function Test-Command {
param (
[string] $Name
)
[bool] (Get-Command $Name -ErrorAction SilentlyContinue);
}
<#
.SYNOPSIS
Checks whether `winget` is working properly.
#>
function Test-Winget {
(Test-Command winget) -and -not (
[System.Linq.Enumerable]::Any(
[string[]](winget source update winget),
[System.Func[string,bool]]{ param($line) $line -eq "Cancelled"; }));
}
<#
.SYNOPSIS
Checks whether a package with the specified name is installed.
.PARAMETER Name
The name of the package to check.
#>
function Test-PSPackage {
param(
[string] $Name
)
[bool] (Get-Package $Name -ErrorAction SilentlyContinue);
}

View file

@ -0,0 +1,31 @@
<#
.SYNOPSIS
Creates a new temporary directory.
#>
function New-TemporaryDirectory {
$path = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName());
New-Item -ItemType Directory $path;
}
<#
.SYNOPSIS
Removes desktop icons which apply to the specified pattern.
.PARAMETER Pattern
The pattern to match the icons to delete.
#>
function Remove-DesktopIcon {
param(
[string] $Pattern
)
$path = "Desktop/$Pattern";
foreach ($userDir in @("~", $env:PUBLIC, "$env:SystemDrive/Users/Default")) {
$fullName = "$userDir/$path";
if (Test-Path -PathType Leaf $fullName) {
Remove-Item $fullName;
}
}
}