diff --git a/scripts/Common/Scripts/Software.ps1 b/scripts/Common/Scripts/Software.ps1
index d8c71fe2..a55abc2f 100644
--- a/scripts/Common/Scripts/Software.ps1
+++ b/scripts/Common/Scripts/Software.ps1
@@ -9,6 +9,70 @@ $null = New-Module {
     . "$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.
@@ -26,24 +90,39 @@ $null = New-Module {
             [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 (Test-ChocoPackage $name) {
-                    Write-Host "Package ``$name`` is already installed"
-                    $Names.RemoveAt($i);
-                }
+        & $chocoRunner @PSBoundParameters -Guard {
+            param($Name)
+            if (Test-ChocoPackage $Name) {
+                Write-Host "Package ``$Name`` is already installed"
+                $false;
+            } else {
+                $true;
             }
-        }
+        };
+    }
 
-        if ($Names.Count -ge 1) {
-            choco install -y @ArgumentList @Names;
-        }
+    <#
+        .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;
+            }
+        };
     }
 
     <#
@@ -63,21 +142,40 @@ $null = New-Module {
             [string[]] $AdditionalNames = @()
         )
 
-        [System.Collections.ArrayList] $Names = @();
-        $null = $Names.Add($Name);
-        $Names.AddRange($AdditionalNames);
+        & $wingetRunner @PSBoundParameters `
+            -Guard {
+                param($Name, $Parameters)
+                if (Test-WingetPackage -Name $Name @Parameters) {
+                    Write-Host "Package ``$Name`` is already installed"
+                    $false;
+                } else {
+                    $true;
+                }
+            };
+    }
 
-        foreach ($name in $Names) {
-            if ($Force.IsPresent -or -not (Test-WingetPackage -Name $name @PSBoundParameters)) {
-                winget install `
-                    --accept-source-agreements --accept-package-agreements `
-                    --source winget `
-                    $ArgumentList `
-                    --exact --id $name ;
+    <#
+        .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 already installed"
+                Write-Host "Package ``$Name`` is not installed"
+                $false;
             }
-        }
+        };
     }
 
     <#