diff --git a/scripts/Common/Scripts/Config.ps1 b/scripts/Common/Scripts/Config.ps1
index e6a86173..90e82ae9 100644
--- a/scripts/Common/Scripts/Config.ps1
+++ b/scripts/Common/Scripts/Config.ps1
@@ -1,56 +1,18 @@
 using namespace Microsoft.Win32;
 using namespace System.Management.Automation.Host;
-using namespace System.Security.AccessControl;
-using namespace System.Security.Principal;
-
-enum WindowsInstallerStage {
-    Initialize
-    Run
-    Cleanup
-    Completed
-}
-
-enum SetupStage {
-    Initialize
-    Configure
-    Install
-    CreateUser
-}
-
-enum BackupStage {
-    Initialize
-    Backup
-    BackupUsers
-}
-
-enum UserStage {
-    Create
-    Configure
-    Cleanup
-    Completed
-}
 
 $null = New-Module {
-    [string] $configRoot = "HKLM:\Software\PortValhalla";
-    [string] $stageOption = "Stage";
-    [string] $setupStageOption = "SetupStage";
-    [string] $backupStageOption = "BackupStage";
-    [string] $userOption = "SetupUser";
-    [string] $userStageOption = "UserStage";
-    [string] $accountOption = "MSAccount";
-    [string] $finishedOption = "Finished";
-    [RegistryKey] $key = $null;
+    . "$PSScriptRoot/SoftwareManagement.ps1";
+    . "$PSScriptRoot/../../Windows/Scripts/Registry.ps1";
+    . "$PSScriptRoot/../../Windows/Types/WindowsInstallerAction.ps1";
 
     <#
         .SYNOPSIS
         Prompts the user to select a profile to act on.
     #>
     function Show-ProfileNamePrompt {
-        . "$PSScriptRoot/../../Windows/Types/WindowsInstallerAction.ps1";
 
         $profiles = & {
-            . "$PSScriptRoot/SoftwareManagement.ps1";
-
             if (-not $IsWindows -or (Test-Command "wsl")) {
                 return Invoke-ConfigScript "getProfiles";
             } else {
@@ -93,75 +55,6 @@ $null = New-Module {
         }
     }
 
-    <#
-        .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
-        )
-
-        & {
-            $ErrorActionPreference = 'Continue';
-            $completed = $false;
-
-            while (-not $completed) {
-                $job = Start-Job {
-                    $env:Value = Resolve-Path $Using:Path;
-                    $env:WSLENV = "Value/p";
-                    $result = wsl -- bash -c 'echo "$Value"';
-                    wsl -e printf "%q" "$result";
-                };
-
-                $result = Receive-Job -Wait $job;
-
-
-                if ((Split-Path -Leaf $Path) -ne (Split-Path -Leaf $result)) {
-                    Write-Error "The result of the path conversion of ``$Path`` was unexpected: ``$result``";
-                    continue;
-                }
-
-                if ($job.State -ne ([System.Management.Automation.JobState]::Completed)) {
-                    Write-Error "An error occurred while converting ``$Path`` to a Linux path.`nOutput: ``$result``";
-                    continue;
-                }
-
-                $completed = $true;
-            }
-
-            $result;
-        };
-    }
-
-    <#
-        .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.
@@ -174,6 +67,7 @@ $null = New-Module {
             [string] $Script
         )
 
+        . "$PSScriptRoot/../../Windows/Scripts/WSL.ps1";
         $scriptPath = "$PSScriptRoot/../../Common/Scripts/config.fish";
 
         if ($env:CONFIG_NAME -or ($Script -eq "getProfiles")) {
@@ -335,273 +229,4 @@ $null = New-Module {
         param()
         Get-OSConfig "setupUser.name";
     }
-
-    <#
-        .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).PSPath;
-
-        if ($null -eq $Value) {
-            Remove-ItemProperty $key -Name $Name;
-        } else {
-            $null = Set-ItemProperty $key -Name $Name -Value $Value;
-        }
-    }
-
-    <#
-        .SYNOPSIS
-        Gets the name of the current stage of the Windows install script action.
-    #>
-    function Get-Stage {
-        $stage = Get-SetupOption $stageOption;
-
-        if ($null -ne $stage) {
-            $stage = [WindowsInstallerStage]$stage;
-        }
-
-        return $stage;
-    }
-
-    <#
-        .SYNOPSIS
-        Sets the name of the current stage of the Windows install script action.
-
-        .PARAMETER Name
-        The name of the stage to set.
-    #>
-    function Set-Stage {
-        param(
-            $Name
-        )
-
-        if (-not (($null -eq $Name) -or ($Name -is [string]))) {
-            $Name = ([WindowsInstallerStage]$Name).ToString();
-        }
-
-        $null = Set-SetupOption $stageOption $Name;
-    }
-
-    <#
-        .SYNOPSIS
-        Gets the name of the current setup stage.
-    #>
-    function Get-SetupStage {
-        $stage = Get-SetupOption $setupStageOption;
-
-        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-SetupStage {
-        param(
-            $Name
-        )
-
-        if (-not (($null -eq $Name) -or ($Name -is [string]))) {
-            $Name = ([SetupStage]$Name).ToString();
-        }
-
-        $null = Set-SetupOption $setupStageOption $Name;
-    }
-
-    <#
-        .SYNOPSIS
-        Gets the name of the current stage of the backup.
-    #>
-    function Get-BackupStage {
-        $stage = Get-SetupOption $backupStageOption;
-
-        if ($null -ne $stage) {
-            $stage = [BackupStage]$stage;
-        }
-
-        return $stage;
-    }
-
-    <#
-        .SYNOPSIS
-        Sets the current stage of the backup.
-
-        .PARAMETER Name
-        The name to set the current stage to.
-    #>
-    function Set-BackupStage {
-        param(
-            $Name
-        )
-
-        if (-not (($null -eq $Name) -or ($Name -is [string]))) {
-            $Name = ([BackupStage]$Name).ToString();
-        }
-
-        $null = Set-SetupOption $backupStageOption $Name;
-    }
-
-    <#
-        .SYNOPSIS
-        Gets the current user to set up.
-    #>
-    function Get-CurrentUser {
-        return (Get-SetupOption $userOption) ?? 0;
-    }
-
-    <#
-        .SYNOPSIS
-        Sets the index of the current user to set up.
-
-        .PARAMETER Value
-        The index of the user to set up.
-    #>
-    function Set-CurrentUser {
-        param(
-            [int] $Value
-        )
-
-        Set-SetupOption $userOption $value;
-    }
-
-    <#
-        .SYNOPSIS
-        Gets the name of the current stage of the user setup.
-    #>
-    function Get-UserStage {
-        $stage = Get-SetupOption $userStageOption;
-
-        if ($null -ne $stage) {
-            $stage = [UserStage]$stage;
-        }
-
-        return $stage;
-    }
-
-    <#
-        .SYNOPSIS
-        Sets the current stage of the user setup.
-
-        .PARAMETER Name
-        The name of the stage to set.
-    #>
-    function Set-UserStage {
-        param(
-            $Name
-        )
-
-        if (-not (($null -eq $Name) -or ($Name -is [string]))) {
-            $Name = ([UserStage]$Name).ToString();
-        }
-
-        $null = Set-SetupOption $userStageOption $Name;
-    }
-
-    <#
-        .SYNOPSIS
-        Gets the name of the microsoft account to create.
-    #>
-    function Get-MSAccountName {
-        return Get-SetupOption $accountOption;
-    }
-
-    <#
-        .SYNOPSIS
-        Sets the name of the microsoft account to create.
-
-        .PARAMETER Name
-        The name of the microsoft account to create.
-    #>
-    function Set-MSAccountName {
-        param(
-            [string] $Name
-        )
-
-        Set-SetupOption $accountOption $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 running system is a QEMU virtual machine.
-    #>
-    function Test-Qemu {
-        ((Get-WmiObject win32_computersystem).Manufacturer) -eq "QEMU";
-    }
-
-    <#
-        .SYNOPSIS
-        Checks whether the current user is the setup user.
-    #>
-    function Test-SetupUser {
-        ($IsWindows ? $env:UserName : $env:USER) -eq (Get-SetupUser);
-    }
-
-    <#
-        .SYNOPSIS
-        Checks whether the active session is executed with admin rights.
-    #>
-    function Test-Admin {
-        net session 2> $null | Out-Null;
-        return $?;
-    }
 }
diff --git a/scripts/Common/Scripts/Operations.ps1 b/scripts/Common/Scripts/Operations.ps1
index 3abee6e4..911e48bd 100644
--- a/scripts/Common/Scripts/Operations.ps1
+++ b/scripts/Common/Scripts/Operations.ps1
@@ -1,35 +1,17 @@
 using namespace System.Management.Automation.Host;
 
-. "$PSScriptRoot/../Types/OneShotTask.ps1";
-
 $null = New-Module {
-    . "$PSScriptRoot/Config.ps1";
     . "$PSScriptRoot/Scripting.ps1";
-    . "$PSScriptRoot/../Scripts/SoftwareManagement.ps1";
-    . "$PSScriptRoot/../Types/OneShotTask.ps1";
+    . "$PSScriptRoot/SoftwareManagement.ps1";
     . "$PSScriptRoot/../../Windows/Scripts/Constants.ps1";
     . "$PSScriptRoot/../../Windows/Scripts/Hooks.ps1";
     . "$PSScriptRoot/../../Windows/Scripts/PowerManagement.ps1";
     . "$PSScriptRoot/../../Windows/Scripts/Registry.ps1";
     . "$PSScriptRoot/../../Windows/Scripts/Security.ps1";
+    . "$PSScriptRoot/../../Windows/Scripts/SoftwareManagement.ps1";
+    . "$PSScriptRoot/../../Windows/Scripts/System.ps1";
+    . "$PSScriptRoot/../../Windows/Scripts/Tasks.ps1";
     . "$PSScriptRoot/../../Windows/Scripts/WSL.ps1";
-    $oneShotTaskName = "PortValhalla OneShot";
-    $logName = "Application";
-    $oneShotTrigger = 1337;
-    $taskOption = "OneShotTask";
-
-    $getErrorPath = {
-        Join-Path (Get-ArtifactRoot) "error.txt";
-    };
-
-    $getUserName = {
-        "$(Get-SetupUser)OneShot";
-    };
-
-    $taskSetter = {
-        param([Nullable[OneShotTask]] $Task)
-        Set-SetupOption $taskOption ([string]$Task);
-    };
 
     <#
         .SYNOPSIS
@@ -289,141 +271,6 @@ $null = New-Module {
         & $cleanup;
     }
 
-    <#
-        .SYNOPSIS
-        Gets the current OneShot task.
-    #>
-    function Get-OneShotTask {
-        $task = Get-SetupOption $taskOption;
-
-        if ($task) {
-            return [OneShotTask]$task;
-        } else {
-            return $null;
-        }
-    }
-
-    <#
-        .SYNOPSIS
-        Registers a task for listening to OneShot invocations.
-    #>
-    function Enable-OneShotListener {
-        $tempTask = "PortValhalla Temp";
-        $user = & $getUserName;
-        $password = [string]([guid]::NewGuid());
-
-        $adminGroup = @{
-            SID = [SecurityIdentifier]::new([WellKnownSidType]::BuiltinAdministratorsSid, $null);
-        };
-
-        $null = New-LocalUser -Name $user -Password (ConvertTo-SecureString -AsPlainText $password);
-        Add-LocalGroupMember -Member $user @adminGroup;
-        $logPath = Join-Path (Get-ArtifactRoot) "OneShotTask.log";
-        $path = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList";
-        $null = New-Item -Force -ErrorAction SilentlyContinue $path;
-        Set-ItemProperty $path -Name $user -Value 0;
-
-        $action = New-ScheduledTaskAction -Execute "pwsh" -Argument "-Command & { $([string](Get-StartupCommand)) } 2>&1 | Tee-Object -FilePath $(ConvertTo-Injection $logPath)";
-        schtasks /Create /SC ONEVENT /EC $logName /MO "*[System[Provider[@Name='$logName'] and EventID=$($oneShotTrigger)]]" /TR cmd.exe /TN $tempTask;
-        $trigger = (Get-ScheduledTask $tempTask).Triggers;
-        $null = Register-ScheduledTask -Force $oneShotTaskName -Action $action -Trigger $trigger -RunLevel Highest -User $user -Password $password;
-        $null = Unregister-ScheduledTask -Confirm:$false $tempTask;
-    }
-
-    <#
-        .SYNOPSIS
-        Removes the OneShot task.
-    #>
-    function Disable-OneShotListener {
-        Unregister-ScheduledTask -Confirm:$false $oneShotTaskName;
-        $user = Get-LocalUser (& $getUserName);
-        [string] $sid = $user.SID;
-        Remove-LocalUser $user;
-        Get-CimInstance Win32_UserProfile | Where-Object { $_.SID -eq $sid } | Remove-CimInstance;
-    }
-
-    <#
-        .SYNOPSIS
-        Invokes a one-shot task.
-
-        .PARAMETER Task
-        The task to run.
-    #>
-    function Invoke-OneShot {
-        param(
-            [OneShotTask] $Task
-        )
-
-        $errorPath = & $getErrorPath;
-        & $taskSetter $Task;
-
-        & {
-            $identifier = "EventLog$oneShotTrigger";
-            $log = [System.Diagnostics.EventLog]::new($logName);
-            $log.EnableRaisingEvents = $true;
-
-            $null = Register-ObjectEvent -InputObject $log -EventName EntryWritten -Action {
-                    $entry = $Event.SourceEventArgs.Entry;
-                    $trigger = $Event.MessageData.Trigger;
-                    $identifier = $Event.MessageData.Identifier;
-
-                    if ($entry.EventID -eq $trigger) {
-                        $null = New-Event -SourceIdentifier $identifier;
-                    }
-                } `
-                -MessageData @{
-                    Trigger = $oneShotTrigger;
-                    Identifier = $identifier;
-                };
-
-            Write-EventLog -LogName $logName -Source $logName -EventId $oneShotTrigger -Message "Starting OneShot task ``$(Get-OneShotTask)``…";
-
-            for ($i = 0; $i -lt 2; $i++) {
-                Remove-Event -EventIdentifier (Wait-Event -SourceIdentifier $identifier).EventIdentifier;
-            }
-        };
-
-        if (Test-Path $errorPath) {
-            $errorMessage = Get-Content $errorPath;
-            Remove-Item $errorPath;
-
-            if ($errorMessage) {
-                Write-Error $errorMessage;
-            }
-        }
-    }
-
-    # ToDo: Store Run-OneShot and Receive-OneShot somewhere else in Windows folder
-
-    <#
-        .SYNOPSIS
-        Executes the specified action and notifies the OneShot task executor.
-    #>
-    function Start-OneShot {
-        try {
-            Write-Host "Running OneShot task ``$(Get-OneShotTask)``";
-
-            switch (Get-OneShotTask) {
-                ([OneShotTask]::InitializeMSAccount) {
-                    Initialize-UserCreation;
-                }
-                ([OneShotTask]::DisableUAC) {
-                    Disable-UAC;
-                    Register-Setup;
-                }
-            }
-        }
-        catch {
-            $errorPath = & $getErrorPath;
-            Set-Content -Path $errorPath -Value $Error;
-            Set-UserPermissions $errorPath;
-        }
-        finally {
-            & $taskSetter $null;
-            Write-EventLog -LogName $logName -Source $logName -EventId $oneShotTrigger -Message "The OneShot task ``$(Get-OneShotTask)`` finished.";
-        }
-    }
-
     <#
         .SYNOPSIS
         Clears resources allocated during the operation.
diff --git a/scripts/Common/Scripts/Software.ps1 b/scripts/Common/Scripts/Software.ps1
index f8e68bfb..4731f8c3 100644
--- a/scripts/Common/Scripts/Software.ps1
+++ b/scripts/Common/Scripts/Software.ps1
@@ -1,297 +1,11 @@
-. "$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";
 
-    $chocoRunner = {
-        param(
-            [string] $Action = 'install',
-            [string[]] $ArgumentList,
-            [scriptblock] $Guard = { $true },
-            [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--) {
-                $name = $Names[$i];
-
-                if (-not (& $Guard $name)) {
-                    $Names.RemoveAt($i);
-                }
-            }
-        }
-
-        if ($Names.Count -ge 1) {
-            choco $Action -y @ArgumentList @Names;
-        }
-    };
-
-    $wingetRunner = {
-        param(
-            [string] $Action = 'install',
-            [string[]] $ArgumentList,
-            [scriptblock] $Guard = { $true },
-            [Parameter(Position = 0)]
-            [string] $Name,
-            [Parameter(ValueFromRemainingArguments = $true)]
-            [string[]] $AdditionalNames = @()
-        )
-
-        [System.Collections.ArrayList] $Names = @();
-        $null = $Names.Add($Name);
-        $Names.AddRange($AdditionalNames);
-
-        [string[]] $arguments = $ArgumentList + (& {
-            if ($Action -eq 'install') {
-                @("--accept-package-agreements")
-            };
-        });
-
-        foreach ($name in $Names) {
-            if ($Force.IsPresent -or (& $Guard $name $PSBoundParameters)) {
-                winget $Action `
-                    --accept-source-agreements `
-                    --source winget `
-                    @arguments `
-                    --exact --id $name ;
-            } else {
-                Write-Host "Package ``$name`` is already installed"
-            }
-        }
-    };
-
-    <#
-        .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 = @()
-        )
-
-        & $chocoRunner @PSBoundParameters -Guard {
-            param($Name)
-            if (Test-ChocoPackage $Name) {
-                Write-Host "Package ``$Name`` is already installed"
-                $false;
-            } else {
-                $true;
-            }
-        };
-    }
-
-    <#
-        .SYNOPSIS
-        Uninstalls the specified packages using chocolatey.
-    #>
-    function Uninstall-ChocoPackage {
-        param(
-            [string[]] $ArgumentList,
-            [Parameter(Position=0)]
-            [string] $Name,
-            [Parameter(ValueFromRemainingArguments = $true)]
-            [string[]] $AdditionalNames = @()
-        )
-
-        & $chocoRunner @PSBoundParameters -Action 'uninstall' -Guard {
-            param($Name)
-            if (Test-ChocoPackage $Name) {
-                $true;
-            } else {
-                Write-Host "Package ``$Name`` is not installed";
-                $false;
-            }
-        };
-    }
-
-    <#
-        .SYNOPSIS
-        Installs the specified packages using `winget`.
-
-        .PARAMETER Names
-        The names of the packages to install.
-    #>
-    function Install-WingetPackage {
-        param(
-            [switch] $Force,
-            [string[]] $ArgumentList,
-            [Parameter(Position=0)]
-            [string] $Name,
-            [Parameter(ValueFromRemainingArguments = $true)]
-            [string[]] $AdditionalNames = @()
-        )
-
-        & $wingetRunner @PSBoundParameters `
-            -Guard {
-                param($Name, $Parameters)
-                if (Test-WingetPackage -Name $Name @Parameters) {
-                    Write-Host "Package ``$Name`` is already installed"
-                    $false;
-                } else {
-                    $true;
-                }
-            };
-    }
-
-    <#
-        .SYNOPSIS
-        Uninstalls the specified packages using `winget`.
-    #>
-    function Uninstall-WingetPackage {
-        param(
-            [string[]] $ArgumentList,
-            [Parameter(Position=0)]
-            [string] $Name,
-            [Parameter(ValueFromRemainingArguments = $true)]
-            [string[]] $AdditionalNames = @()
-        )
-
-        & $wingetRunner @PSBoundParameters -Action 'uninstall' -Guard {
-            param($Name, $Parameters)
-            if (Test-WingetPackage -Name $Name @Parameters) {
-                $true;
-            } else {
-                Write-Host "Package ``$Name`` is not installed"
-                $false;
-            }
-        };
-    }
-
-    <#
-        .SYNOPSIS
-        Installs a setup package from the specified source.
-
-        .PARAMETER Source
-        The location of the setup package.
-
-        .PARAMETER ArgumentList
-        The arguments to pass to the setup package.
-
-        .PARAMETER Local
-        A value indicating whether the setup package is stored locally.
-    #>
-    function Install-SetupPackage {
-        param(
-            [string] $Source,
-            [string[]] $ArgumentList = @("/S"),
-            [switch] $Local
-        )
-
-        [string] $dir = $null;
-        [string] $filePath = $null;
-
-        if (-not ($Local.IsPresent)) {
-            $dir = New-TemporaryDirectory;
-            Write-Host "Determining the file name of ``$Source``…";
-            $fileName = ([uri]$Source).Segments[-1];
-            Write-Host "Found name ``$fileName``";
-            $filePath = Join-Path $dir $fileName;
-
-            Write-Host "Downloading setup file from ``$Source``";
-            Invoke-WebRequest $Source -OutFile $filePath;
-        } else {
-            $filePath = $Source;
-        }
-
-        Write-Host "Starting installation of ``$(Split-Path -Leaf $filePath)``";
-        Start-Process -Wait -WorkingDirectory (Split-Path -Parent $filePath) -FilePath $filePath -ArgumentList $ArgumentList;
-
-        if ($dir) {
-            Remove-Item -Recurse $dir;
-        }
-    }
-
-    <#
-        .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 {
-                $null = $Browser.ExecuteScript("document.querySelector('$cookieBannerSelector').remove()");
-                & $download;
-            }
-        };
-
-        Start-Process -Wait -WorkingDirectory (Split-Path -Parent "$file") -FilePath "$file" -ArgumentList "/S";
-        Remove-Item -Recurse $dir;
-    }
-
     function Start-SoftwareInstaller {
         param(
             [string] $Name,
diff --git a/scripts/Common/Scripts/SoftwareManagement.ps1 b/scripts/Common/Scripts/SoftwareManagement.ps1
index 3689438a..8f53f7d7 100644
--- a/scripts/Common/Scripts/SoftwareManagement.ps1
+++ b/scripts/Common/Scripts/SoftwareManagement.ps1
@@ -1,36 +1,3 @@
-<#
-    .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] $Name,
-        [string[]] $ArgumentList
-    )
-
-    & { $null = winget list --accept-source-agreements -e --id $Name @ArgumentList; $?; };
-}
-
 <#
     .SYNOPSIS
     Checks whether a command with the specified name exists.
@@ -46,21 +13,6 @@ function Test-Command {
     [bool] (Get-Command $Name -ErrorAction SilentlyContinue);
 }
 
-<#
-    .SYNOPSIS
-    Checks whether `winget` is working properly.
-#>
-function Test-Winget {
-    (Test-Command winget) -and (
-        & {
-            $output = winget source update winget;
-
-            $? -and -not ([System.Linq.Enumerable]::Any(
-                [string[]]($output),
-                [System.Func[string,bool]]{ param($line) $line -eq "Cancelled"; }));
-        });
-}
-
 <#
     .SYNOPSIS
     Checks whether a package with the specified name is installed.
diff --git a/scripts/Common/Scripts/System.ps1 b/scripts/Common/Scripts/System.ps1
index 2fa7db88..9dca7e8c 100644
--- a/scripts/Common/Scripts/System.ps1
+++ b/scripts/Common/Scripts/System.ps1
@@ -9,60 +9,8 @@ function New-TemporaryDirectory {
 
 <#
     .SYNOPSIS
-    Removes desktop icons which apply to the specified pattern.
-
-    .PARAMETER Pattern
-    The pattern to match the icons to delete.
+    Checks whether the current user is the setup user.
 #>
-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;
-        }
-    }
-}
-
-<#
-    .SYNOPSIS
-    Adds a new shortcut to the start menu.
-
-    .PARAMETER Name
-    The name of the icon to create.
-
-    .PARAMETER Target
-    The file to link to.
-#>
-function Add-StartMenuIcon {
-    param(
-        [string] $Name,
-        [string] $Target
-    )
-
-    Import-Module KnownFolders;
-    Import-Module "$env:ChocolateyInstall/helpers/chocolateyInstaller.psm1";
-    Install-ChocolateyShortcut -ShortcutFilePath "$((Get-KnownFolder "Common Programs").Path)/$Name.lnk" -TargetPath ((Get-Item $Target).FullName);
-}
-
-<#
-    .SYNOPSIS
-    Removes icons from the task bar.
-
-    .PARAMETER Pattern
-    The pattern of the icon names to remove.
-#>
-function Remove-TaskbarItem {
-    param(
-        [string] $Pattern
-    )
-
-    Import-Module -UseWindowsPowerShell PinnedItem;
-    Get-PinnedItem -Type TaskBar | Where-Object { $_.Name -like "$Pattern" } | ForEach-Object { Remove-PinnedItem $_ };
+function Test-SetupUser {
+    ($IsWindows ? $env:UserName : $env:USER) -eq (Get-SetupUser);
 }
diff --git a/scripts/Windows/Drivers/Tobii EyeX/Manage.ps1 b/scripts/Windows/Drivers/Tobii EyeX/Manage.ps1
index 8755f3a9..c812bdf5 100644
--- a/scripts/Windows/Drivers/Tobii EyeX/Manage.ps1	
+++ b/scripts/Windows/Drivers/Tobii EyeX/Manage.ps1	
@@ -6,6 +6,7 @@ param(
 & {
     param($parameters);
 
+    . "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
     . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
     $softwarePath = "$PSScriptRoot/../../Software";
 
diff --git a/scripts/Windows/OS/Manage.ps1 b/scripts/Windows/OS/Manage.ps1
index 5d715cb4..6c86573f 100644
--- a/scripts/Windows/OS/Manage.ps1
+++ b/scripts/Windows/OS/Manage.ps1
@@ -10,13 +10,14 @@ $null = New-Module {
     . "$PSScriptRoot/../Scripts/PowerManagement.ps1";
     . "$PSScriptRoot/../Scripts/Registry.ps1";
     . "$PSScriptRoot/../Scripts/Security.ps1";
+    . "$PSScriptRoot/../Scripts/SoftwareManagement.ps1";
+    . "$PSScriptRoot/../Scripts/System.ps1";
     . "$PSScriptRoot/../Scripts/Update.ps1";
     . "$PSScriptRoot/../Scripts/Users.ps1";
     . "$PSScriptRoot/../Types/WindowsInstallerAction.ps1";
     . "$PSScriptRoot/../../Common/Scripts/Config.ps1";
     . "$PSScriptRoot/../../Common/Scripts/Operations.ps1";
     . "$PSScriptRoot/../../Common/Scripts/Scripting.ps1";
-    . "$PSScriptRoot/../../Common/Scripts/Software.ps1";
     . "$PSScriptRoot/../../Common/Scripts/SoftwareManagement.ps1";
     . "$PSScriptRoot/../../Common/Types/InstallerAction.ps1";
 
diff --git a/scripts/Windows/Scripts/Deployment.ps1 b/scripts/Windows/Scripts/Deployment.ps1
index fb8655c4..dab15880 100644
--- a/scripts/Windows/Scripts/Deployment.ps1
+++ b/scripts/Windows/Scripts/Deployment.ps1
@@ -1,5 +1,5 @@
 . "$PSScriptRoot/PowerManagement.ps1";
-. "$PSScriptRoot/../../Common/Scripts/Software.ps1";
+. "$PSScriptRoot/SoftwareManagement.ps1";
 . "$PSScriptRoot/../../Common/Scripts/SoftwareManagement.ps1";
 . "$PSScriptRoot/../../Common/Types/InstallerAction.ps1";
 
diff --git a/scripts/Windows/Scripts/PowerManagement.ps1 b/scripts/Windows/Scripts/PowerManagement.ps1
index 45e003a3..e83e2933 100644
--- a/scripts/Windows/Scripts/PowerManagement.ps1
+++ b/scripts/Windows/Scripts/PowerManagement.ps1
@@ -2,7 +2,6 @@ using namespace Microsoft.Win32;
 
 $null = New-Module {
     . "$PSScriptRoot/../Scripts/Registry.ps1";
-    . "$PSScriptRoot/../../Common/Scripts/Config.ps1";
     . "$PSScriptRoot/../../Common/Scripts/Scripting.ps1";
     [RegistryKey] $key = $null;
     $runOncePath = "Software\Microsoft\Windows\CurrentVersion\RunOnce";
diff --git a/scripts/Windows/Scripts/Registry.ps1 b/scripts/Windows/Scripts/Registry.ps1
index 2ce9b9e6..1e4adbd7 100644
--- a/scripts/Windows/Scripts/Registry.ps1
+++ b/scripts/Windows/Scripts/Registry.ps1
@@ -1,6 +1,45 @@
 using namespace Microsoft.Win32;
+using namespace System.Security.AccessControl;
+using namespace System.Security.Principal;
+
+enum WindowsInstallerStage {
+    Initialize
+    Run
+    Cleanup
+    Completed
+}
+
+enum SetupStage {
+    Initialize
+    Configure
+    Install
+    CreateUser
+}
+
+enum BackupStage {
+    Initialize
+    Backup
+    BackupUsers
+}
+
+enum UserStage {
+    Create
+    Configure
+    Cleanup
+    Completed
+}
 
 $null = New-Module {
+    [string] $configRoot = "HKLM:\Software\PortValhalla";
+    [string] $stageOption = "Stage";
+    [string] $setupStageOption = "SetupStage";
+    [string] $backupStageOption = "BackupStage";
+    [string] $userOption = "SetupUser";
+    [string] $userStageOption = "UserStage";
+    [string] $accountOption = "MSAccount";
+    [string] $finishedOption = "Finished";
+    [RegistryKey] $key = $null;
+
     function Edit-DefaultUserKey {
         param(
             [scriptblock] $Action
@@ -17,6 +56,31 @@ $null = New-Module {
         & reg unload $regRootPath;
     }
 
+    <#
+        .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
         Sets a message to show on the login screen.
@@ -53,4 +117,248 @@ $null = New-Module {
     function Disable-BootMessage {
         Set-BootMessage;
     }
+
+    <#
+        .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).PSPath;
+
+        if ($null -eq $Value) {
+            Remove-ItemProperty $key -Name $Name;
+        } else {
+            $null = Set-ItemProperty $key -Name $Name -Value $Value;
+        }
+    }
+
+    <#
+        .SYNOPSIS
+        Gets the name of the current stage of the Windows install script action.
+    #>
+    function Get-Stage {
+        $stage = Get-SetupOption $stageOption;
+
+        if ($null -ne $stage) {
+            $stage = [WindowsInstallerStage]$stage;
+        }
+
+        return $stage;
+    }
+
+    <#
+        .SYNOPSIS
+        Sets the name of the current stage of the Windows install script action.
+
+        .PARAMETER Name
+        The name of the stage to set.
+    #>
+    function Set-Stage {
+        param(
+            $Name
+        )
+
+        if (-not (($null -eq $Name) -or ($Name -is [string]))) {
+            $Name = ([WindowsInstallerStage]$Name).ToString();
+        }
+
+        $null = Set-SetupOption $stageOption $Name;
+    }
+
+    <#
+        .SYNOPSIS
+        Gets the name of the current setup stage.
+    #>
+    function Get-SetupStage {
+        $stage = Get-SetupOption $setupStageOption;
+
+        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-SetupStage {
+        param(
+            $Name
+        )
+
+        if (-not (($null -eq $Name) -or ($Name -is [string]))) {
+            $Name = ([SetupStage]$Name).ToString();
+        }
+
+        $null = Set-SetupOption $setupStageOption $Name;
+    }
+
+    <#
+        .SYNOPSIS
+        Gets the name of the current stage of the backup.
+    #>
+    function Get-BackupStage {
+        $stage = Get-SetupOption $backupStageOption;
+
+        if ($null -ne $stage) {
+            $stage = [BackupStage]$stage;
+        }
+
+        return $stage;
+    }
+
+    <#
+        .SYNOPSIS
+        Sets the current stage of the backup.
+
+        .PARAMETER Name
+        The name to set the current stage to.
+    #>
+    function Set-BackupStage {
+        param(
+            $Name
+        )
+
+        if (-not (($null -eq $Name) -or ($Name -is [string]))) {
+            $Name = ([BackupStage]$Name).ToString();
+        }
+
+        $null = Set-SetupOption $backupStageOption $Name;
+    }
+
+    <#
+        .SYNOPSIS
+        Gets the current user to set up.
+    #>
+    function Get-CurrentUser {
+        return (Get-SetupOption $userOption) ?? 0;
+    }
+
+    <#
+        .SYNOPSIS
+        Sets the index of the current user to set up.
+
+        .PARAMETER Value
+        The index of the user to set up.
+    #>
+    function Set-CurrentUser {
+        param(
+            [int] $Value
+        )
+
+        Set-SetupOption $userOption $value;
+    }
+
+    <#
+        .SYNOPSIS
+        Gets the name of the current stage of the user setup.
+    #>
+    function Get-UserStage {
+        $stage = Get-SetupOption $userStageOption;
+
+        if ($null -ne $stage) {
+            $stage = [UserStage]$stage;
+        }
+
+        return $stage;
+    }
+
+    <#
+        .SYNOPSIS
+        Sets the current stage of the user setup.
+
+        .PARAMETER Name
+        The name of the stage to set.
+    #>
+    function Set-UserStage {
+        param(
+            $Name
+        )
+
+        if (-not (($null -eq $Name) -or ($Name -is [string]))) {
+            $Name = ([UserStage]$Name).ToString();
+        }
+
+        $null = Set-SetupOption $userStageOption $Name;
+    }
+
+    <#
+        .SYNOPSIS
+        Gets the name of the microsoft account to create.
+    #>
+    function Get-MSAccountName {
+        return Get-SetupOption $accountOption;
+    }
+
+    <#
+        .SYNOPSIS
+        Sets the name of the microsoft account to create.
+
+        .PARAMETER Name
+        The name of the microsoft account to create.
+    #>
+    function Set-MSAccountName {
+        param(
+            [string] $Name
+        )
+
+        Set-SetupOption $accountOption $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;
+    }
 }
diff --git a/scripts/Windows/Scripts/SoftwareManagement.ps1 b/scripts/Windows/Scripts/SoftwareManagement.ps1
new file mode 100644
index 00000000..f86f86dc
--- /dev/null
+++ b/scripts/Windows/Scripts/SoftwareManagement.ps1
@@ -0,0 +1,335 @@
+$null = New-Module {
+    . "$PSScriptRoot/../../Common/Scripts/BrowserAutomation.ps1";
+    . "$PSScriptRoot/../../Common/Scripts/SoftwareManagement.ps1";
+
+    $chocoRunner = {
+        param(
+            [string] $Action = 'install',
+            [string[]] $ArgumentList,
+            [scriptblock] $Guard = { $true },
+            [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--) {
+                $name = $Names[$i];
+
+                if (-not (& $Guard $name)) {
+                    $Names.RemoveAt($i);
+                }
+            }
+        }
+
+        if ($Names.Count -ge 1) {
+            choco $Action -y @ArgumentList @Names;
+        }
+    };
+
+    $wingetRunner = {
+        param(
+            [string] $Action = 'install',
+            [string[]] $ArgumentList,
+            [scriptblock] $Guard = { $true },
+            [Parameter(Position = 0)]
+            [string] $Name,
+            [Parameter(ValueFromRemainingArguments = $true)]
+            [string[]] $AdditionalNames = @()
+        )
+
+        [System.Collections.ArrayList] $Names = @();
+        $null = $Names.Add($Name);
+        $Names.AddRange($AdditionalNames);
+
+        [string[]] $arguments = $ArgumentList + (& {
+            if ($Action -eq 'install') {
+                @("--accept-package-agreements")
+            };
+        });
+
+        foreach ($name in $Names) {
+            if ($Force.IsPresent -or (& $Guard $name $PSBoundParameters)) {
+                winget $Action `
+                    --accept-source-agreements `
+                    --source winget `
+                    @arguments `
+                    --exact --id $name ;
+            } else {
+                Write-Host "Package ``$name`` is already installed"
+            }
+        }
+    };
+
+    <#
+        .SYNOPSIS
+        Checks whether `winget` is working properly.
+    #>
+    function Test-Winget {
+        (Test-Command winget) -and (
+            & {
+                $output = winget source update winget;
+
+                $? -and -not ([System.Linq.Enumerable]::Any(
+                    [string[]]($output),
+                    [System.Func[string,bool]]{ param($line) $line -eq "Cancelled"; }));
+            });
+    }
+
+    <#
+        .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] $Name,
+            [string[]] $ArgumentList
+        )
+
+        & { $null = winget list --accept-source-agreements -e --id $Name @ArgumentList; $?; };
+    }
+
+    <#
+        .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 = @()
+        )
+
+        & $chocoRunner @PSBoundParameters -Guard {
+            param($Name)
+            if (Test-ChocoPackage $Name) {
+                Write-Host "Package ``$Name`` is already installed"
+                $false;
+            } else {
+                $true;
+            }
+        };
+    }
+
+    <#
+        .SYNOPSIS
+        Uninstalls the specified packages using chocolatey.
+    #>
+    function Uninstall-ChocoPackage {
+        param(
+            [string[]] $ArgumentList,
+            [Parameter(Position=0)]
+            [string] $Name,
+            [Parameter(ValueFromRemainingArguments = $true)]
+            [string[]] $AdditionalNames = @()
+        )
+
+        & $chocoRunner @PSBoundParameters -Action 'uninstall' -Guard {
+            param($Name)
+            if (Test-ChocoPackage $Name) {
+                $true;
+            } else {
+                Write-Host "Package ``$Name`` is not installed";
+                $false;
+            }
+        };
+    }
+
+    <#
+        .SYNOPSIS
+        Installs the specified packages using `winget`.
+
+        .PARAMETER Names
+        The names of the packages to install.
+    #>
+    function Install-WingetPackage {
+        param(
+            [switch] $Force,
+            [string[]] $ArgumentList,
+            [Parameter(Position=0)]
+            [string] $Name,
+            [Parameter(ValueFromRemainingArguments = $true)]
+            [string[]] $AdditionalNames = @()
+        )
+
+        & $wingetRunner @PSBoundParameters `
+            -Guard {
+                param($Name, $Parameters)
+                if (Test-WingetPackage -Name $Name @Parameters) {
+                    Write-Host "Package ``$Name`` is already installed"
+                    $false;
+                } else {
+                    $true;
+                }
+            };
+    }
+
+    <#
+        .SYNOPSIS
+        Uninstalls the specified packages using `winget`.
+    #>
+    function Uninstall-WingetPackage {
+        param(
+            [string[]] $ArgumentList,
+            [Parameter(Position=0)]
+            [string] $Name,
+            [Parameter(ValueFromRemainingArguments = $true)]
+            [string[]] $AdditionalNames = @()
+        )
+
+        & $wingetRunner @PSBoundParameters -Action 'uninstall' -Guard {
+            param($Name, $Parameters)
+            if (Test-WingetPackage -Name $Name @Parameters) {
+                $true;
+            } else {
+                Write-Host "Package ``$Name`` is not installed"
+                $false;
+            }
+        };
+    }
+
+    <#
+        .SYNOPSIS
+        Installs a setup package from the specified source.
+
+        .PARAMETER Source
+        The location of the setup package.
+
+        .PARAMETER ArgumentList
+        The arguments to pass to the setup package.
+
+        .PARAMETER Local
+        A value indicating whether the setup package is stored locally.
+    #>
+    function Install-SetupPackage {
+        param(
+            [string] $Source,
+            [string[]] $ArgumentList = @("/S"),
+            [switch] $Local
+        )
+
+        [string] $dir = $null;
+        [string] $filePath = $null;
+
+        if (-not ($Local.IsPresent)) {
+            $dir = New-TemporaryDirectory;
+            Write-Host "Determining the file name of ``$Source``…";
+            $fileName = ([uri]$Source).Segments[-1];
+            Write-Host "Found name ``$fileName``";
+            $filePath = Join-Path $dir $fileName;
+
+            Write-Host "Downloading setup file from ``$Source``";
+            Invoke-WebRequest $Source -OutFile $filePath;
+        } else {
+            $filePath = $Source;
+        }
+
+        Write-Host "Starting installation of ``$(Split-Path -Leaf $filePath)``";
+        Start-Process -Wait -WorkingDirectory (Split-Path -Parent $filePath) -FilePath $filePath -ArgumentList $ArgumentList;
+
+        if ($dir) {
+            Remove-Item -Recurse $dir;
+        }
+    }
+
+    <#
+        .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 {
+                $null = $Browser.ExecuteScript("document.querySelector('$cookieBannerSelector').remove()");
+                & $download;
+            }
+        };
+
+        Start-Process -Wait -WorkingDirectory (Split-Path -Parent "$file") -FilePath "$file" -ArgumentList "/S";
+        Remove-Item -Recurse $dir;
+    }
+};
diff --git a/scripts/Windows/Scripts/System.ps1 b/scripts/Windows/Scripts/System.ps1
new file mode 100644
index 00000000..5bbf99b6
--- /dev/null
+++ b/scripts/Windows/Scripts/System.ps1
@@ -0,0 +1,76 @@
+<#
+    .SYNOPSIS
+    Checks whether the running system is a QEMU virtual machine.
+#>
+function Test-Qemu {
+    ((Get-WmiObject win32_computersystem).Manufacturer) -eq "QEMU";
+}
+
+<#
+    .SYNOPSIS
+    Checks whether the active session is executed with admin rights.
+#>
+function Test-Admin {
+    net session 2> $null | Out-Null;
+    return $?;
+}
+
+<#
+    .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;
+        }
+    }
+}
+
+<#
+    .SYNOPSIS
+    Adds a new shortcut to the start menu.
+
+    .PARAMETER Name
+    The name of the icon to create.
+
+    .PARAMETER Target
+    The file to link to.
+#>
+function Add-StartMenuIcon {
+    param(
+        [string] $Name,
+        [string] $Target
+    )
+
+    Import-Module KnownFolders;
+    Import-Module "$env:ChocolateyInstall/helpers/chocolateyInstaller.psm1";
+    Install-ChocolateyShortcut -ShortcutFilePath "$((Get-KnownFolder "Common Programs").Path)/$Name.lnk" -TargetPath ((Get-Item $Target).FullName);
+}
+
+<#
+    .SYNOPSIS
+    Removes icons from the task bar.
+
+    .PARAMETER Pattern
+    The pattern of the icon names to remove.
+#>
+function Remove-TaskbarItem {
+    param(
+        [string] $Pattern
+    )
+
+    Import-Module -UseWindowsPowerShell PinnedItem;
+    Get-PinnedItem -Type TaskBar | Where-Object { $_.Name -like "$Pattern" } | ForEach-Object { Remove-PinnedItem $_ };
+}
diff --git a/scripts/Windows/Scripts/Tasks.ps1 b/scripts/Windows/Scripts/Tasks.ps1
new file mode 100644
index 00000000..0c912ed6
--- /dev/null
+++ b/scripts/Windows/Scripts/Tasks.ps1
@@ -0,0 +1,156 @@
+$null = New-Module {
+    . "$PSScriptRoot/Constants.ps1";
+    . "$PSScriptRoot/Registry.ps1";
+    . "$PSScriptRoot/../Types/OneShotTask.ps1";
+    . "$PSScriptRoot/../../Common/Scripts/Config.ps1";
+    $oneShotTaskName = "PortValhalla OneShot";
+    $logName = "Application";
+    $oneShotTrigger = 1337;
+    $taskOption = "OneShotTask";
+
+    $getErrorPath = {
+        Join-Path (Get-ArtifactRoot) "error.txt";
+    };
+
+    $getUserName = {
+        "$(Get-SetupUser)OneShot";
+    };
+
+    $taskSetter = {
+        param([Nullable[OneShotTask]] $Task)
+        Set-SetupOption $taskOption ([string]$Task);
+    };
+
+    <#
+        .SYNOPSIS
+        Gets the current OneShot task.
+    #>
+    function Get-OneShotTask {
+        $task = Get-SetupOption $taskOption;
+
+        if ($task) {
+            return [OneShotTask]$task;
+        } else {
+            return $null;
+        }
+    }
+
+    <#
+        .SYNOPSIS
+        Registers a task for listening to OneShot invocations.
+    #>
+    function Enable-OneShotListener {
+        $tempTask = "PortValhalla Temp";
+        $user = & $getUserName;
+        $password = [string]([guid]::NewGuid());
+
+        $adminGroup = @{
+            SID = [SecurityIdentifier]::new([WellKnownSidType]::BuiltinAdministratorsSid, $null);
+        };
+
+        $null = New-LocalUser -Name $user -Password (ConvertTo-SecureString -AsPlainText $password);
+        Add-LocalGroupMember -Member $user @adminGroup;
+        $logPath = Join-Path (Get-ArtifactRoot) "OneShotTask.log";
+        $path = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList";
+        $null = New-Item -Force -ErrorAction SilentlyContinue $path;
+        Set-ItemProperty $path -Name $user -Value 0;
+
+        $action = New-ScheduledTaskAction -Execute "pwsh" -Argument "-Command & { $([string](Get-StartupCommand)) } 2>&1 | Tee-Object -FilePath $(ConvertTo-Injection $logPath)";
+        schtasks /Create /SC ONEVENT /EC $logName /MO "*[System[Provider[@Name='$logName'] and EventID=$($oneShotTrigger)]]" /TR cmd.exe /TN $tempTask;
+        $trigger = (Get-ScheduledTask $tempTask).Triggers;
+        $null = Register-ScheduledTask -Force $oneShotTaskName -Action $action -Trigger $trigger -RunLevel Highest -User $user -Password $password;
+        $null = Unregister-ScheduledTask -Confirm:$false $tempTask;
+    }
+
+    <#
+        .SYNOPSIS
+        Removes the OneShot task.
+    #>
+    function Disable-OneShotListener {
+        Unregister-ScheduledTask -Confirm:$false $oneShotTaskName;
+        $user = Get-LocalUser (& $getUserName);
+        [string] $sid = $user.SID;
+        Remove-LocalUser $user;
+        Get-CimInstance Win32_UserProfile | Where-Object { $_.SID -eq $sid } | Remove-CimInstance;
+    }
+
+    <#
+        .SYNOPSIS
+        Invokes a one-shot task.
+
+        .PARAMETER Task
+        The task to run.
+    #>
+    function Invoke-OneShot {
+        param(
+            [OneShotTask] $Task
+        )
+
+        $errorPath = & $getErrorPath;
+        & $taskSetter $Task;
+
+        & {
+            $identifier = "EventLog$oneShotTrigger";
+            $log = [System.Diagnostics.EventLog]::new($logName);
+            $log.EnableRaisingEvents = $true;
+
+            $null = Register-ObjectEvent -InputObject $log -EventName EntryWritten -Action {
+                    $entry = $Event.SourceEventArgs.Entry;
+                    $trigger = $Event.MessageData.Trigger;
+                    $identifier = $Event.MessageData.Identifier;
+
+                    if ($entry.EventID -eq $trigger) {
+                        $null = New-Event -SourceIdentifier $identifier;
+                    }
+                } `
+                -MessageData @{
+                    Trigger = $oneShotTrigger;
+                    Identifier = $identifier;
+                };
+
+            Write-EventLog -LogName $logName -Source $logName -EventId $oneShotTrigger -Message "Starting OneShot task ``$(Get-OneShotTask)``…";
+
+            for ($i = 0; $i -lt 2; $i++) {
+                Remove-Event -EventIdentifier (Wait-Event -SourceIdentifier $identifier).EventIdentifier;
+            }
+        };
+
+        if (Test-Path $errorPath) {
+            $errorMessage = Get-Content $errorPath;
+            Remove-Item $errorPath;
+
+            if ($errorMessage) {
+                Write-Error $errorMessage;
+            }
+        }
+    }
+
+    <#
+        .SYNOPSIS
+        Executes the specified action and notifies the OneShot task executor.
+    #>
+    function Start-OneShot {
+        try {
+            Write-Host "Running OneShot task ``$(Get-OneShotTask)``";
+
+            switch (Get-OneShotTask) {
+                ([OneShotTask]::InitializeMSAccount) {
+                    Initialize-UserCreation;
+                }
+                ([OneShotTask]::DisableUAC) {
+                    Disable-UAC;
+                    Register-Setup;
+                }
+            }
+        }
+        catch {
+            $errorPath = & $getErrorPath;
+            Set-Content -Path $errorPath -Value $Error;
+            Set-UserPermissions $errorPath;
+        }
+        finally {
+            & $taskSetter $null;
+            Write-EventLog -LogName $logName -Source $logName -EventId $oneShotTrigger -Message "The OneShot task ``$(Get-OneShotTask)`` finished.";
+        }
+    }
+}
diff --git a/scripts/Windows/Scripts/Users.ps1 b/scripts/Windows/Scripts/Users.ps1
index ba6bf6f4..0334e171 100644
--- a/scripts/Windows/Scripts/Users.ps1
+++ b/scripts/Windows/Scripts/Users.ps1
@@ -3,9 +3,11 @@ using namespace System.Security.Principal;
 
 $null = New-Module {
     . "$PSScriptRoot/Deployment.ps1";
+    . "$PSScriptRoot/Registry.ps1";
+    . "$PSScriptRoot/System.ps1";
+    . "$PSScriptRoot/../Types/OneShotTask.ps1";
     . "$PSScriptRoot/../../Common/Scripts/Config.ps1";
     . "$PSScriptRoot/../../Common/Scripts/Operations.ps1";
-    . "$PSScriptRoot/../../Common/Types/OneShotTask.ps1";
     $loggedInUserOption = "LoggedInUser";
 
     <#
diff --git a/scripts/Windows/Scripts/WSL.ps1 b/scripts/Windows/Scripts/WSL.ps1
index eb84e838..bbce2654 100644
--- a/scripts/Windows/Scripts/WSL.ps1
+++ b/scripts/Windows/Scripts/WSL.ps1
@@ -1,5 +1,6 @@
 . "$PSScriptRoot/Constants.ps1";
 . "$PSScriptRoot/../Scripts/Security.ps1";
+. "$PSScriptRoot/../../Common/Scripts/SoftwareManagement.ps1";
 
 <#
     .SYNOPSIS
@@ -57,7 +58,6 @@ function Install-Wsl {
     wsl --install --no-launch;
     # Microsoft broke WSL - Quelle surprise!
     # ToDo: Remove this workaround once it's unbroken
-    . "$PSScriptRoot/../../Common/Scripts/Software.ps1";
     Install-SetupPackage "https://github.com/microsoft/WSL/releases/download/2.3.17/wsl.2.3.17.0.x64.msi" -ArgumentList "/Quiet";
 }
 
@@ -125,3 +125,47 @@ function Unregister-WslDistribution {
     Uninstall-WslDistribution;
     Move-Item $tempDisk $wslDisk;
 }
+
+<#
+    .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
+    )
+
+    & {
+        $ErrorActionPreference = 'Continue';
+        $completed = $false;
+
+        while (-not $completed) {
+            $job = Start-Job {
+                $env:Value = Resolve-Path $Using:Path;
+                $env:WSLENV = "Value/p";
+                $result = wsl -- bash -c 'echo "$Value"';
+                wsl -e printf "%q" "$result";
+            };
+
+            $result = Receive-Job -Wait $job;
+
+
+            if ((Split-Path -Leaf $Path) -ne (Split-Path -Leaf $result)) {
+                Write-Error "The result of the path conversion of ``$Path`` was unexpected: ``$result``";
+                continue;
+            }
+
+            if ($job.State -ne ([System.Management.Automation.JobState]::Completed)) {
+                Write-Error "An error occurred while converting ``$Path`` to a Linux path.`nOutput: ``$result``";
+                continue;
+            }
+
+            $completed = $true;
+        }
+
+        $result;
+    };
+}
diff --git a/scripts/Windows/Software/Ext4Fsd/Main.ps1 b/scripts/Windows/Software/Ext4Fsd/Main.ps1
index 8cba965a..46e2f4bc 100644
--- a/scripts/Windows/Software/Ext4Fsd/Main.ps1
+++ b/scripts/Windows/Software/Ext4Fsd/Main.ps1
@@ -3,6 +3,7 @@ param (
     [hashtable] $Arguments
 )
 
+. "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
 . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
 . "$PSScriptRoot/../../../Common/Types/InstallerAction.ps1";
 
diff --git a/scripts/Windows/Software/Firefox/Manage.ps1 b/scripts/Windows/Software/Firefox/Manage.ps1
index beb6105b..b9334605 100644
--- a/scripts/Windows/Software/Firefox/Manage.ps1
+++ b/scripts/Windows/Software/Firefox/Manage.ps1
@@ -4,6 +4,7 @@ param(
 )
 
 . "$PSScriptRoot/../../Scripts/AppAssociations.ps1";
+. "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
 . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
 
 Start-SoftwareInstaller @PSBoundParameters `
diff --git a/scripts/Windows/Software/LGHub/Manage.ps1 b/scripts/Windows/Software/LGHub/Manage.ps1
index 07403926..b0358d15 100644
--- a/scripts/Windows/Software/LGHub/Manage.ps1
+++ b/scripts/Windows/Software/LGHub/Manage.ps1
@@ -6,6 +6,7 @@ param(
 & {
     param ($Parameters)
     . "$PSScriptRoot/../../Scripts/Restoration.ps1";
+    . "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
     . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
     $configPath = "$env:LocalAppData/LGHUB";
 
diff --git a/scripts/Windows/Software/MSEdgeRedirect/Manage.ps1 b/scripts/Windows/Software/MSEdgeRedirect/Manage.ps1
index e87fa1df..55a7a32e 100644
--- a/scripts/Windows/Software/MSEdgeRedirect/Manage.ps1
+++ b/scripts/Windows/Software/MSEdgeRedirect/Manage.ps1
@@ -3,6 +3,7 @@ param(
     [hashtable] $Arguments
 )
 
+. "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
 . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
 
 Start-SoftwareInstaller @PSBoundParameters `
diff --git a/scripts/Windows/Software/ManiaPlanet/Manage.ps1 b/scripts/Windows/Software/ManiaPlanet/Manage.ps1
index ccb93b76..d55e10df 100644
--- a/scripts/Windows/Software/ManiaPlanet/Manage.ps1
+++ b/scripts/Windows/Software/ManiaPlanet/Manage.ps1
@@ -6,6 +6,7 @@ param(
 & {
     param($Parameters)
     . "$PSScriptRoot/../../Scripts/Restoration.ps1";
+    . "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
     . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
     . "$PSScriptRoot/../../../Common/Scripts/System.ps1";
     $path = "$HOME/Documents/ManiaPlanet";
diff --git a/scripts/Windows/Software/Oh My Posh/Manage.ps1 b/scripts/Windows/Software/Oh My Posh/Manage.ps1
index 47784d60..84d5ce3a 100644
--- a/scripts/Windows/Software/Oh My Posh/Manage.ps1	
+++ b/scripts/Windows/Software/Oh My Posh/Manage.ps1	
@@ -3,6 +3,7 @@ param (
     [hashtable] $Arguments
 )
 
+. "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
 . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
 . "$PSScriptRoot/../../../Common/Types/InstallerAction.ps1";
 
diff --git a/scripts/Windows/Software/PuTTY/Manage.ps1 b/scripts/Windows/Software/PuTTY/Manage.ps1
index a957cedc..41718da3 100644
--- a/scripts/Windows/Software/PuTTY/Manage.ps1
+++ b/scripts/Windows/Software/PuTTY/Manage.ps1
@@ -6,6 +6,7 @@ param(
 )
 
 . "$PSScriptRoot/../../Scripts/Restoration.ps1";
+. "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
 . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
 . "$PSScriptRoot/../../../Common/Scripts/System.ps1";
 
diff --git a/scripts/Windows/Software/RetroArch/Manage.ps1 b/scripts/Windows/Software/RetroArch/Manage.ps1
index 6a94e002..c69e0870 100644
--- a/scripts/Windows/Software/RetroArch/Manage.ps1
+++ b/scripts/Windows/Software/RetroArch/Manage.ps1
@@ -6,6 +6,7 @@ param(
 & {
     param($Parameters)
     . "$PSScriptRoot/../../Scripts/Restoration.ps1";
+    . "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
     . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
     . "$PSScriptRoot/../../../Common/Scripts/System.ps1";
     $path = "C:/tools/RetroArch-Win64";
diff --git a/scripts/Windows/Software/Thunderbird/Manage.ps1 b/scripts/Windows/Software/Thunderbird/Manage.ps1
index dc11372a..37549b79 100644
--- a/scripts/Windows/Software/Thunderbird/Manage.ps1
+++ b/scripts/Windows/Software/Thunderbird/Manage.ps1
@@ -6,6 +6,7 @@ param(
 )
 
 . "$PSScriptRoot/../../Scripts/AppAssociations.ps1";
+. "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
 . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
 
 Start-SoftwareInstaller @PSBoundParameters `
diff --git a/scripts/Windows/Software/TobiiGameHub/Manage.ps1 b/scripts/Windows/Software/TobiiGameHub/Manage.ps1
index 1be0388e..e572c179 100644
--- a/scripts/Windows/Software/TobiiGameHub/Manage.ps1
+++ b/scripts/Windows/Software/TobiiGameHub/Manage.ps1
@@ -5,6 +5,7 @@ param(
     [hashtable] $Arguments
 )
 
+. "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
 . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
 . "$PSScriptRoot/../../../Common/Scripts/System.ps1";
 
diff --git a/scripts/Windows/Software/TobiiGhost/Manage.ps1 b/scripts/Windows/Software/TobiiGhost/Manage.ps1
index 9d0960a7..02f0255b 100644
--- a/scripts/Windows/Software/TobiiGhost/Manage.ps1
+++ b/scripts/Windows/Software/TobiiGhost/Manage.ps1
@@ -3,6 +3,7 @@ param(
     [hashtable] $Arguments
 )
 
+. "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
 . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
 . "$PSScriptRoot/../../../Common/Scripts/System.ps1";
 
diff --git a/scripts/Windows/Software/TrackMania Nations Forever/Manage.ps1 b/scripts/Windows/Software/TrackMania Nations Forever/Manage.ps1
index df75d5cc..fbbb4e51 100644
--- a/scripts/Windows/Software/TrackMania Nations Forever/Manage.ps1	
+++ b/scripts/Windows/Software/TrackMania Nations Forever/Manage.ps1	
@@ -7,6 +7,7 @@ param(
 & {
     param($Parameters)
     . "$PSScriptRoot/../../Scripts/Restoration.ps1";
+    . "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
     . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
     $path = "$HOME/Documents/TmForever";
 
diff --git a/scripts/Windows/Software/Ubiquiti UniFi Controller/Manage.ps1 b/scripts/Windows/Software/Ubiquiti UniFi Controller/Manage.ps1
index eb047168..3b64f46a 100644
--- a/scripts/Windows/Software/Ubiquiti UniFi Controller/Manage.ps1	
+++ b/scripts/Windows/Software/Ubiquiti UniFi Controller/Manage.ps1	
@@ -3,6 +3,7 @@ param(
     [hashtable] $Arguments
 )
 
+. "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
 . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
 . "$PSScriptRoot/../../../Common/Scripts/System.ps1";
 
diff --git a/scripts/Windows/Software/VisualStudio/Manage.ps1 b/scripts/Windows/Software/VisualStudio/Manage.ps1
index d430c02f..4afad841 100644
--- a/scripts/Windows/Software/VisualStudio/Manage.ps1
+++ b/scripts/Windows/Software/VisualStudio/Manage.ps1
@@ -7,6 +7,7 @@ param(
     param($parameters)
 
     . "$PSScriptRoot/../../Scripts/Restoration.ps1";
+    . "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
     . "$PSScriptRoot/../../../Common/Scripts/BrowserAutomation.ps1";
     . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
     . "$PSScriptRoot/../../../Common/Scripts/SoftwareManagement.ps1";
diff --git a/scripts/Windows/Software/WinSCP/Manage.ps1 b/scripts/Windows/Software/WinSCP/Manage.ps1
index 2d977288..dabe433a 100644
--- a/scripts/Windows/Software/WinSCP/Manage.ps1
+++ b/scripts/Windows/Software/WinSCP/Manage.ps1
@@ -6,6 +6,7 @@ param(
 )
 
 . "$PSScriptRoot/../../Scripts/AppAssociations.ps1";
+. "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
 . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
 . "$PSScriptRoot/../../../Common/Scripts/System.ps1";
 
diff --git a/scripts/Windows/Software/aliae/Main.ps1 b/scripts/Windows/Software/aliae/Main.ps1
index 2143ea1c..341a329a 100644
--- a/scripts/Windows/Software/aliae/Main.ps1
+++ b/scripts/Windows/Software/aliae/Main.ps1
@@ -3,6 +3,7 @@ param (
     [hashtable] $Arguments
 )
 
+. "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
 . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
 . "$PSScriptRoot/../../../Common/Types/InstallerAction.ps1";
 
diff --git a/scripts/Windows/Software/git/Manage.ps1 b/scripts/Windows/Software/git/Manage.ps1
index 320c0f93..1edc3858 100644
--- a/scripts/Windows/Software/git/Manage.ps1
+++ b/scripts/Windows/Software/git/Manage.ps1
@@ -3,6 +3,7 @@ param (
     [hashtable] $Arguments
 )
 
+. "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
 . "$PSScriptRoot/../../../Common/Scripts/Config.ps1";
 . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
 . "$PSScriptRoot/../../../Common/Types/InstallerAction.ps1";
diff --git a/scripts/Windows/Software/osu!/Manage.ps1 b/scripts/Windows/Software/osu!/Manage.ps1
index f5db941b..6be11cef 100644
--- a/scripts/Windows/Software/osu!/Manage.ps1
+++ b/scripts/Windows/Software/osu!/Manage.ps1
@@ -3,6 +3,7 @@ param(
     [hashtable] $Arguments
 )
 
+. "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
 . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
 
 & {
diff --git a/scripts/Windows/Software/osu!lazer/Manage.ps1 b/scripts/Windows/Software/osu!lazer/Manage.ps1
index b6f8556d..726531ad 100644
--- a/scripts/Windows/Software/osu!lazer/Manage.ps1
+++ b/scripts/Windows/Software/osu!lazer/Manage.ps1
@@ -6,6 +6,7 @@ param(
 & {
     param($Parameters)
     . "$PSScriptRoot/../../Scripts/Restoration.ps1";
+    . "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
     . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
     . "$PSScriptRoot/../../../Common/Scripts/System.ps1";
     $path = "$env:AppData/osu";
diff --git a/scripts/Windows/Software/vscode/Main.ps1 b/scripts/Windows/Software/vscode/Main.ps1
index 96141b58..d091d5de 100644
--- a/scripts/Windows/Software/vscode/Main.ps1
+++ b/scripts/Windows/Software/vscode/Main.ps1
@@ -8,6 +8,7 @@ param(
         [hashtable] $Parameters
     )
 
+    . "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
     . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
     . "$PSScriptRoot/../../../Common/Types/InstallerAction.ps1";
     $base = "$PSScriptRoot/../../../Common/Software/vscode/Main.ps1";
diff --git a/scripts/Windows/Software/zoxide/Manage.ps1 b/scripts/Windows/Software/zoxide/Manage.ps1
index 07d5eb99..6a0cce02 100644
--- a/scripts/Windows/Software/zoxide/Manage.ps1
+++ b/scripts/Windows/Software/zoxide/Manage.ps1
@@ -8,6 +8,7 @@ param(
         [hashtable] $Parameters
     )
 
+    . "$PSScriptRoot/../../Scripts/SoftwareManagement.ps1";
     . "$PSScriptRoot/../../../Common/Scripts/Software.ps1";
     . "$PSScriptRoot/../../../Common/Software/PowerShell/Profile.ps1";
     . "$PSScriptRoot/../../../Common/Types/InstallerAction.ps1";
diff --git a/scripts/Common/Types/OneShotTask.ps1 b/scripts/Windows/Types/OneShotTask.ps1
similarity index 100%
rename from scripts/Common/Types/OneShotTask.ps1
rename to scripts/Windows/Types/OneShotTask.ps1