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 } } |