How to Install Disk Cleanup Utility on Windows Server 2012 Without Desktop Experience for VHDX Optimization


2 views

When preparing virtual hard disks (VHDX) for optimization in Windows Server 2012, many administrators want to use the built-in Disk Cleanup utility (cleanmgr.exe) to remove unnecessary files before running Optimize-VHD. However, Microsoft bundles this tool with the Desktop Experience feature, which brings unwanted dependencies like Media Foundation and Ink and Handwriting components.

You can manually extract and register the Disk Cleanup components without installing the full Desktop Experience:

# Copy required files from Windows installation media
robocopy "D:\sources\install.wim\Windows\WinSxS" "C:\Windows\WinSxS" /E /COPYALL /R:0 /W:0 /NP /XO /XD amd64_microsoft-windows-a..ence-mitigations-c1_* amd64_microsoft-windows-a..ence-mitigations-c2_*

# Register the DLL
regsvr32 /s C:\Windows\WinSxS\amd64_microsoft-windows-cleanmgr_*\cleanmgr.exe

# Create the scheduled task XML (required for proper execution)
$cleanmgrXML = @'
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <RegistrationInfo>
    <Description>Disk Cleanup</Description>
  </RegistrationInfo>
  <Principals>
    <Principal id="Author">
      <UserId>S-1-5-18</UserId>
      <RunLevel>HighestAvailable</RunLevel>
    </Principal>
  </Principals>
  <Settings>
    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
    <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
    <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
    <AllowHardTerminate>true</AllowHardTerminate>
    <StartWhenAvailable>false</StartWhenAvailable>
    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
    <IdleSettings>
      <StopOnIdleEnd>true</StopOnIdleEnd>
      <RestartOnIdle>false</RestartOnIdle>
    </IdleSettings>
    <AllowStartOnDemand>true</AllowStartOnDemand>
    <Enabled>true</Enabled>
    <Hidden>false</Hidden>
    <RunOnlyIfIdle>false</RunOnlyIfIdle>
    <WakeToRun>false</WakeToRun>
    <ExecutionTimeLimit>P3D</ExecutionTimeLimit>
    <Priority>7</Priority>
  </Settings>
  <Actions Context="Author">
    <Exec>
      <Command>C:\Windows\System32\cleanmgr.exe</Command>
    </Exec>
  </Actions>
</Task>
'@
$cleanmgrXML | Out-File "C:\Windows\System32\cleanmgr.xml"

For those preferring pure PowerShell, here's a function that mimics Disk Cleanup's functionality:

function Invoke-VHDXCleanup {
    param (
        [Parameter(Mandatory=$true)]
        [string]$DriveLetter
    )
    
    # Clear Windows Update cache
    if (Test-Path "$DriveLetter\Windows\SoftwareDistribution\Download") {
        Remove-Item "$DriveLetter\Windows\SoftwareDistribution\Download\*" -Recurse -Force
    }
    
    # Clear temporary files
    if (Test-Path "$DriveLetter\Windows\Temp") {
        Remove-Item "$DriveLetter\Windows\Temp\*" -Recurse -Force
    }
    
    # Clear user temp files (for all users)
    Get-ChildItem "$DriveLetter\Users" | ForEach-Object {
        $userTemp = "$($_.FullName)\AppData\Local\Temp"
        if (Test-Path $userTemp) {
            Remove-Item "$userTemp\*" -Recurse -Force
        }
    }
    
    # Clear IIS logs if present
    if (Test-Path "$DriveLetter\inetpub\logs") {
        Remove-Item "$DriveLetter\inetpub\logs\*" -Recurse -Force
    }
    
    # Clear .NET temporary files
    if (Test-Path "$DriveLetter\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files") {
        Remove-Item "$DriveLetter\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\*" -Recurse -Force
    }
}

After cleaning the disk, run the VHDX optimization:

# First check fragmentation level
Get-VHD -Path "C:\VMs\server2012.vhdx" | Select-Object Path,FileSize,Size,@{Name="Fragmentation";Expression={[math]::Round(($_.FileSize/$_.Size)*100,2)}}

# Then optimize
Optimize-VHD -Path "C:\VMs\server2012.vhdx" -Mode Full

To confirm the cleanup was effective:

# Check disk space before and after
$before = Get-WmiObject Win32_LogicalDisk -Filter "DeviceID='C:'" | Select-Object Size,FreeSpace
# Run cleanup...
$after = Get-WmiObject Win32_LogicalDisk -Filter "DeviceID='C:'" | Select-Object Size,FreeSpace
$spaceRecovered = ($after.FreeSpace - $before.FreeSpace)/1GB
Write-Host "Recovered $([math]::Round($spaceRecovered,2)) GB of disk space"

When preparing virtual machine VHDX files for optimization with Optimize-VHD, admins often need to reclaim space by removing temporary files. While Microsoft's Disk Cleanup (cleanmgr.exe) is ideal for this, it's frustratingly tied to the bloated Desktop Experience feature on Windows Server 2012.

Installing Desktop Experience introduces unnecessary components like:

- Media Foundation (increases attack surface)
- Ink and Handwriting Services (irrelevant for servers)
- Additional GUI components (wastes resources)

You can extract just the Disk Cleanup functionality by manually registering its components:

# From elevated PowerShell:
Copy-Item "$env:windir\winsxs\amd64_microsoft-windows-cleanmgr_*" "C:\Temp\CleanMgr"
Copy-Item "$env:windir\winsxs\amd64_microsoft-windows-cleanmgr.resources_*" "C:\Temp\CleanMgr\en-US"
regsvr32 /s "C:\Temp\CleanMgr\cleanmgr.exe"
regsvr32 /s "C:\Temp\CleanMgr\cleanmgr.exe.mui"

Combine this with your optimization workflow:

# PowerShell script example
function Optimize-VHDX {
    param([string]$VHDPath)
    
    # Mount VHDX
    $disk = Mount-VHD -Path $VHDPath -Passthru
    $partition = Get-Partition -DiskNumber $disk.DiskNumber | Where-Object {$_.Type -eq 'Basic'}
    
    # Run Disk Cleanup
    Start-Process "cleanmgr" -ArgumentList "/sagerun:1" -Wait
    
    # Dismount and optimize
    Dismount-VHD -Path $VHDPath
    Optimize-VHD -Path $VHDPath -Mode Full
}

Create custom cleanup profiles by first running:

cleanmgr /sageset:1

Then reference this config in scripts using /sagerun:1. This avoids interactive prompts during automation.

For completely headless environments, consider these PowerShell alternatives:

# Delete temporary files
Remove-Item "$env:windir\Temp\*" -Recurse -Force
Remove-Item "$env:TEMP\*" -Recurse -Force

# Clear Windows Update cache
Stop-Service wuauserv
Remove-Item "$env:windir\SoftwareDistribution\Download\*" -Recurse -Force
Start-Service wuauserv

After cleanup, verify reclaimed space with:

Get-VHD -Path "C:\VMs\server.vhdx" | Select-Object Path,FileSize,Size