private/Software/Install-MicrosoftDeploymentToolkit.ps1

<#
.SYNOPSIS
Installs Microsoft Deployment Toolkit (MDT) from module metadata.
 
.DESCRIPTION
Downloads the MDT x64 MSI from the URL defined in module metadata,
validates the SHA256 checksum, and installs it silently using msiexec.
 
.OUTPUTS
System.Management.Automation.PSCustomObject
 
.EXAMPLE
Install-MicrosoftDeploymentToolkit
 
Downloads, verifies, and installs MDT if it is not already installed.
 
.NOTES
Author: David Segura
Company: Recast Software
This function is supported only on Windows.
Microsoft Deployment Toolkit (MDT) has an immediate retirement notice from Microsoft:
https://learn.microsoft.com/en-us/troubleshoot/mem/configmgr/mdt/mdt-retirement
 
.LINK
https://learn.microsoft.com/en-us/intune/configmgr/mdt/
 
.LINK
https://learn.microsoft.com/en-us/troubleshoot/mem/configmgr/mdt/mdt-retirement
 
.LINK
https://web.archive.org/web/20250616094712/https://download.microsoft.com/download/3/3/9/339BE62D-B4B8-4956-B58D-73C4685FC492/MicrosoftDeploymentToolkit_x64.msi
Change Summary:
#>

function Install-MicrosoftDeploymentToolkit {
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    [OutputType([pscustomobject])]
    param (
        [switch] $DownloadOnly
    )

    if (-not $IsWindows) {
        throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Install-MicrosoftDeploymentToolkit is supported only on Windows."
    }

    $currentPrincipal = [Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
    if (-not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
        throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Install-MicrosoftDeploymentToolkit requires Administrator rights. Re-run PowerShell as Administrator and try again."
    }

    $curl = Get-Command -Name 'curl.exe' -ErrorAction SilentlyContinue
    if (-not $curl) {
        throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] curl.exe is required but was not found. Ensure curl.exe is available in PATH (included with Windows 10 1803+)."
    }

    $msiExec = Get-Command -Name 'msiexec.exe' -ErrorAction SilentlyContinue
    if (-not $msiExec) {
        throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] msiexec.exe is required but was not found."
    }

    if (-not $global:OSDeployModule -or -not $global:OSDeployModule.Software.mdt) {
        throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] OSDeployCore module metadata is missing required mdt configuration."
    }

    $mdtConfig = $global:OSDeployModule.Software.mdt
    $retirementUrl = [string]$mdtConfig.retirement
    $mdtUrl = [string]$mdtConfig.msi
    $expectedSha256 = ([string]$mdtConfig.sha256).ToLowerInvariant()
    $expectedVersion = [string]$mdtConfig.version

    Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Microsoft Deployment Toolkit (MDT) has an immediate retirement notice from Microsoft."
    Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] $retirementUrl"

    if ([string]::IsNullOrWhiteSpace($mdtUrl) -or [string]::IsNullOrWhiteSpace($expectedSha256) -or [string]::IsNullOrWhiteSpace($expectedVersion)) {
        throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] OSDeployCore module metadata mdt is incomplete. Required keys: msi, sha256, version."
    }

    $uninstallPaths = @(
        'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
        'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
    )

    $installedMdt = $null
    foreach ($uninstallPath in $uninstallPaths) {
        $installedMdt = Get-ItemProperty -Path $uninstallPath -ErrorAction SilentlyContinue |
            Where-Object { $_.PSObject.Properties['DisplayName'] -and $_.DisplayName -like 'Microsoft Deployment Toolkit*' } |
            Select-Object -First 1

        if ($installedMdt) {
            break
        }
    }

    if ($installedMdt) {
        $installedVersion = [string]$installedMdt.DisplayVersion
        if ($installedVersion -eq $expectedVersion) {
            Write-Host "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Microsoft Deployment Toolkit is already installed: $installedVersion (expected: $expectedVersion)" -ForegroundColor Green
        }
        else {
            Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Microsoft Deployment Toolkit is installed but version mismatch. Installed: $installedVersion, Expected: $expectedVersion"
        }
    }

    $downloadDir = Join-Path -Path $Script:OSDeployCoreSoftwarePath -ChildPath "Microsoft.DeploymentToolkit_$expectedVersion"
    New-Item -Path $downloadDir -ItemType Directory -Force | Out-Null
    $installerPath = Join-Path -Path $downloadDir -ChildPath 'MicrosoftDeploymentToolkit_x64.msi'
    $skippedInstall = $false

    if ($PSCmdlet.ShouldProcess('Microsoft Deployment Toolkit', 'Download, verify SHA256, and install silently')) {
        Write-Host "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Downloading Microsoft Deployment Toolkit MSI..." -ForegroundColor DarkGray
        & $curl.Source --insecure --location --output $installerPath --url $mdtUrl
        if ($LASTEXITCODE -ne 0) {
            throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Failed to download MDT MSI (curl.exe exit code $LASTEXITCODE)."
        }

        $actualSha256 = (Get-FileHash -Path $installerPath -Algorithm SHA256).Hash.ToLowerInvariant()
        if ($actualSha256 -ne $expectedSha256) {
            throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] MDT MSI checksum mismatch. Expected $expectedSha256 but got $actualSha256."
        }

        if ($DownloadOnly) {
            Write-Host "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] DownloadOnly: skipping installation." -ForegroundColor DarkGray
            return [pscustomobject]@{
                ProductName    = 'Microsoft Deployment Toolkit'
                Version        = $expectedVersion
                WasInstalled   = $false
                SkippedInstall = $false
                InstallerPath  = $installerPath
                InstallerUrl   = $mdtUrl
                Sha256         = $actualSha256
                DownloadOnly   = $true
            }
        }

        if ($installedMdt) {
            # MDT already installed; skip re-install
            return [pscustomobject]@{
                ProductName    = $installedMdt.DisplayName
                Version        = $installedVersion
                WasInstalled   = $false
                SkippedInstall = $true
                InstallerPath  = $installerPath
                InstallerUrl   = $mdtUrl
                Sha256         = $actualSha256
            }
        }

        Write-Host "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Installing Microsoft Deployment Toolkit..." -ForegroundColor DarkGray
        $msiArgs = @('/i', $installerPath, '/qn', '/norestart')
        $process = Start-Process -FilePath $msiExec.Source -ArgumentList $msiArgs -Wait -PassThru
        if ($process.ExitCode -ne 0) {
            throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] MDT installation failed with exit code $($process.ExitCode)."
        }

        Update-OSDeploySessionEnvironment

        $installedMdt = $null
        foreach ($uninstallPath in $uninstallPaths) {
            $installedMdt = Get-ItemProperty -Path $uninstallPath -ErrorAction SilentlyContinue |
                Where-Object { $_.PSObject.Properties['DisplayName'] -and $_.DisplayName -like 'Microsoft Deployment Toolkit*' } |
                Select-Object -First 1

            if ($installedMdt) {
                break
            }
        }

        if (-not $installedMdt) {
            throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] MDT install completed but product was not found in uninstall registry."
        }

        $installedVersion = [string]$installedMdt.DisplayVersion
        if ($installedVersion -ne $expectedVersion) {
            throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] MDT installation completed but version mismatch. Installed: $installedVersion, Expected: $expectedVersion."
        }

        Write-Host "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Microsoft Deployment Toolkit installed successfully: $installedVersion" -ForegroundColor Green

        return [pscustomobject]@{
            ProductName    = $installedMdt.DisplayName
            Version        = $installedVersion
            WasInstalled   = $true
            SkippedInstall = $false
            InstallerPath  = $installerPath
            InstallerUrl   = $mdtUrl
            Sha256         = $actualSha256
        }
    }

    $skippedInstall = $true
    [pscustomobject]@{
        ProductName    = 'Microsoft Deployment Toolkit'
        Version        = $null
        WasInstalled   = $false
        SkippedInstall = $skippedInstall
        InstallerPath  = $installerPath
        InstallerUrl   = $mdtUrl
        Sha256         = $expectedSha256
    }
}