public/Install-OSDeploySoftware.ps1
|
#Requires -PSEdition Core #Requires -Version 7.4 <# .SYNOPSIS Installs OS deployment prerequisite software on Windows. .DESCRIPTION Install-OSDeploySoftware is the single entry point for setting up a Windows workstation with all tools commonly required for OS deployment work. When called without -Force, the function runs in preview mode: it returns a table showing each requested component's name, download source, documentation link, and the exact command needed to install it. This lets you review what will happen before committing to any change. Add -Force to perform the actual installation. Administrator rights are required for ADK, MDT, and Hyper-V installations; winget must be available for Git for Windows and Visual Studio Code installs. Supported components: adk-25h2 Windows ADK 10.1.26100.2454 (25H2) + WinPE add-on, downloaded with curl. adk-26h1 Windows ADK 10.1.28000.1 (26H1) + WinPE add-on, downloaded with curl. mdt Microsoft Deployment Toolkit 6.3.8456.1000, installed from a verified MSI. git Git for Windows, installed via winget with optional global identity setup. code Visual Studio Code (stable channel), installed via winget. code-insiders Visual Studio Code Insiders (pre-release channel), installed via winget. hyperv Hyper-V, enabled as a Windows optional feature. .PARAMETER Name One or more component names to inspect or install. Accepts the alias 'Component'. Valid values: adk-25h2, adk-26h1, mdt, git, code, code-insiders, hyperv When omitted, returns a list of all available components and the command needed to install each one. .PARAMETER Force Performs the installation. Without this switch the command runs in preview mode and returns source, documentation, and installation details only. .PARAMETER DownloadOnly Downloads the installer(s) to the component-named cache subfolder without installing. For components that require curl (adk-25h2, adk-26h1, mdt), the file is saved to C:\ProgramData\OSDeployCore\cache\downloads\<name>\ and installation is skipped. winget- and feature-based components (git, code, code-insiders, hyperv) do not support this switch; a note object is returned. Can be combined with -Force to re-download even when a cached file exists. .OUTPUTS System.Management.Automation.PSCustomObject Without -Force: returns one object per component with Name, Component, Action, Source, Docs, Details, Note, and Command properties. With -Force: returns one object per component with Component and Status properties. When no -Name is supplied: returns one object per available component with Name, FullName, Action, and Command properties. .INPUTS None. This function does not accept pipeline input. .EXAMPLE Install-OSDeploySoftware Returns a list of all available components, their full names, and the install command for each. No changes are made to the system. .EXAMPLE Install-OSDeploySoftware -Name 'adk-26h1' Returns installation source and documentation details for Windows ADK 26H1. No changes are made to the system. Add -Force to install. .EXAMPLE Install-OSDeploySoftware -Name 'adk-26h1' -Force Downloads and installs Windows ADK 10.1.28000.1 and its Windows PE add-on. Requires Administrator rights. .EXAMPLE Install-OSDeploySoftware -Name 'git', 'code', 'code-insiders' -Force Installs Git for Windows, Visual Studio Code, and VS Code Insiders in sequence. Requires winget (App Installer from the Microsoft Store). .EXAMPLE Install-OSDeploySoftware -Name 'mdt' Returns the MDT download source, retirement notice link, and the exact command to run to install. No changes are made to the system. .EXAMPLE Install-OSDeploySoftware -Name 'adk-26h1' -DownloadOnly Downloads adksetup.exe and adkwinpesetup.exe to C:\ProgramData\OSDeployCore\cache\downloads\adk-26h1\ without installing. Requires Administrator rights. .EXAMPLE Install-OSDeploySoftware -Name 'mdt' -DownloadOnly Downloads and SHA256-verifies MicrosoftDeploymentToolkit_x64.msi to C:\ProgramData\OSDeployCore\cache\downloads\mdt\ without installing. Requires Administrator rights. .EXAMPLE Install-OSDeploySoftware -Name 'mdt' -Force Downloads, SHA256-verifies, and silently installs Microsoft Deployment Toolkit 6.3.8456.1000. Requires Administrator rights. IMPORTANT: MDT has been officially retired by Microsoft. Install it only if your existing workflow depends on it. For new deployments consider using Microsoft Intune or Windows Autopilot instead. .NOTES - Run PowerShell 7 as Administrator before using -Force with ADK, MDT, or Hyper-V. - winget (App Installer) must be installed for git, code, and code-insiders. - ADK and MDT use curl.exe for downloads; curl.exe ships with Windows 10 1803+. - ADK installs also apply the MDT WinPE x86 MMC snap-in fix automatically. - MDT is officially retired. See the retirement notice link in the Details property. Author: David Segura Company: Recast Software .LINK https://learn.microsoft.com/en-us/windows-hardware/get-started/adk-install .LINK https://learn.microsoft.com/en-us/troubleshoot/mem/configmgr/mdt/mdt-retirement #> function Install-OSDeploySoftware { [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param ( [Alias('Component')] [ValidateSet( 'adk-25h2', 'adk-26h1', 'mdt', 'git', 'code', 'code-insiders', 'hyperv', '7zip' )] [string[]] $Name, [switch] $Force, [switch] $DownloadOnly ) Write-OSDeployBanner # Auto-download pwsh installer every time unless already cached # TODO: re-enable when ready # try { # $pwshWinget = Get-Command -Name 'winget' -ErrorAction SilentlyContinue # if ($pwshWinget) { # $pwshSoftwarePath = Join-Path -Path $script:OSDeployCoreSoftwarePath -ChildPath 'Microsoft.PowerShell' # $pwshAlreadyCached = (Test-Path -Path $pwshSoftwarePath) -and # (Get-ChildItem -Path $pwshSoftwarePath -File -ErrorAction SilentlyContinue | Select-Object -First 1) # if (-not $pwshAlreadyCached) { # New-Item -Path $pwshSoftwarePath -ItemType Directory -Force | Out-Null # $pwshPackageId = if ($global:OSDeployModule -and $global:OSDeployModule.Software.pwsh.wingetid) { # [string]$global:OSDeployModule.Software.pwsh.wingetid # } else { 'Microsoft.PowerShell' } # Write-Host "[$(Get-Date -format s)] [Install-OSDeploySoftware] Downloading $pwshPackageId to $pwshSoftwarePath..." -ForegroundColor DarkGray # & $pwshWinget.Source download --id $pwshPackageId --download-directory $pwshSoftwarePath --accept-source-agreements --accept-package-agreements # } # } else { # Write-Verbose '[Install-OSDeploySoftware] winget not found; skipping auto-download of pwsh.' # } # } catch { # Write-Verbose "[Install-OSDeploySoftware] pwsh auto-download skipped: $_" # } $availableComponents = @( 'adk-25h2' 'adk-26h1' 'mdt' 'git' 'code' 'code-insiders' 'hyperv' '7zip' ) if (-not $PSBoundParameters.ContainsKey('Name') -or -not $Name -or $Name.Count -eq 0) { $componentFullName = @{ 'pwsh' = 'PowerShell 7' 'PowerShell 7' = 'PowerShell 7' 'adk-25h2' = 'Windows ADK 25H2' 'adk-26h1' = 'Windows ADK 26H1' 'mdt' = 'Microsoft Deployment Toolkit' 'git' = 'Git for Windows' 'code' = 'Visual Studio Code' 'code-insiders' = 'Visual Studio Code Insiders' 'hyperv' = 'Hyper-V' '7zip' = '7-Zip' } $options = foreach ($option in $availableComponents) { [pscustomobject]@{ Name = $option FullName = $componentFullName[$option] Action = 'Install' Command = "Install-OSDeploySoftware -Name '$option'" } } return $options } $componentFullName = @{ 'pwsh' = 'PowerShell 7' 'PowerShell 7' = 'PowerShell 7' 'adk-25h2' = 'Windows ADK 25H2' 'adk-26h1' = 'Windows ADK 26H1' 'mdt' = 'Microsoft Deployment Toolkit' 'git' = 'Git for Windows' 'code' = 'Visual Studio Code' 'code-insiders' = 'Visual Studio Code Insiders' 'hyperv' = 'Hyper-V' 'Hyper-V' = 'Hyper-V' '7zip' = '7-Zip' '7-Zip' = '7-Zip' } $componentMetadata = @{ 'pwsh' = @{ Component = 'PowerShell 7' Source = $global:OSDeployModule.Software.pwsh.wingetid Docs = $global:OSDeployModule.Software.pwsh.docs Details = $global:OSDeployModule.Software.pwsh.wiki } 'adk-25h2' = @{ Component = 'Windows ADK 25H2' Source = $global:OSDeployModule.Software.adk.'25h2'.adksetup Docs = $global:OSDeployModule.Software.adk.docs Details = $global:OSDeployModule.Software.adk.'25h2'.wiki } 'adk-26h1' = @{ Component = 'Windows ADK 26H1' Source = $global:OSDeployModule.Software.adk.'26h1'.adksetup Docs = $global:OSDeployModule.Software.adk.docs Details = $global:OSDeployModule.Software.adk.'26h1'.wiki } 'mdt' = @{ Component = 'Microsoft Deployment Toolkit' Source = $global:OSDeployModule.Software.mdt.msi Docs = $global:OSDeployModule.Software.mdt.docs Details = $global:OSDeployModule.Software.mdt.retirement } 'git' = @{ Component = 'Git for Windows' Source = $global:OSDeployModule.Software.git.wingetid Docs = 'https://git-scm.com/download/win' Details = 'https://winget.run/pkg/Git/Git' } 'hyperv' = @{ Component = 'Hyper-V' Source = $global:OSDeployModule.Software.hyperv.featurename Docs = $global:OSDeployModule.Software.hyperv.docs Details = $global:OSDeployModule.Software.hyperv.docs } 'code' = @{ Component = 'Visual Studio Code' Source = $global:OSDeployModule.Software.vscode.stable.wingetid Docs = $global:OSDeployModule.Software.vscode.docs Details = $global:OSDeployModule.Software.vscode.wiki } 'code-insiders' = @{ Component = 'Visual Studio Code Insiders' Source = $global:OSDeployModule.Software.vscode.insiders.wingetid Docs = $global:OSDeployModule.Software.vscode.docs Details = $global:OSDeployModule.Software.vscode.wiki } '7zip' = @{ Component = '7-Zip' Source = $global:OSDeployModule.Software.'7zip'.id Docs = $global:OSDeployModule.Software.'7zip'.releases Details = $global:OSDeployModule.BootImage.winpeapps.sevenzip.standalone } } if (-not $Force -and -not $DownloadOnly) { $preview = foreach ($item in $Name) { $metadata = $componentMetadata[$item] [pscustomobject]@{ Name = $item Component = $metadata.Component Action = 'Preview' Source = $metadata.Source Docs = $metadata.Docs Details = $metadata.Details Note = 'Add -Force to install or -DownloadOnly to download only.' Command = "Install-OSDeploySoftware -Name '$item' -Force" } } return $preview } Assert-OSDeployAdministrator $results = [System.Collections.Generic.List[pscustomobject]]::new() # Components that use Windows features or winget (which downloads to %TEMP%\WinGet) have no separate downloadable asset. $downloadOnlyUnsupported = @('hyperv', 'git', 'code', 'code-insiders') foreach ($item in $Name) { if ($DownloadOnly -and $item -in $downloadOnlyUnsupported) { $results.Add([pscustomobject]@{ Name = $item Component = $componentFullName[$item] Action = 'DownloadOnly' Status = 'NotSupported' Note = '-DownloadOnly is not supported for Windows feature-based components.' }) continue } $resolvedComponent = if ($componentFullName.ContainsKey($item)) { $componentFullName[$item] } else { $item } switch ($resolvedComponent) { 'Windows ADK 25H2' { if ($PSCmdlet.ShouldProcess('Windows ADK 25H2', ($DownloadOnly ? 'Download' : 'Install'))) { Install-MicrosoftWindowsAdk25H2 -DownloadOnly:$DownloadOnly $results.Add([pscustomobject]@{ Component = 'Windows ADK 25H2' Status = ($DownloadOnly ? 'Downloaded' : 'Installed') }) } } 'Windows ADK 26H1' { if ($PSCmdlet.ShouldProcess('Windows ADK 26H1', ($DownloadOnly ? 'Download' : 'Install'))) { Install-MicrosoftWindowsAdk26H1 -DownloadOnly:$DownloadOnly $results.Add([pscustomobject]@{ Component = 'Windows ADK 26H1' Status = ($DownloadOnly ? 'Downloaded' : 'Installed') }) } } 'Microsoft Deployment Toolkit' { if ($PSCmdlet.ShouldProcess('Microsoft Deployment Toolkit', ($DownloadOnly ? 'Download' : 'Install'))) { Install-MicrosoftDeploymentToolkit -DownloadOnly:$DownloadOnly $results.Add([pscustomobject]@{ Component = 'Microsoft Deployment Toolkit' Status = ($DownloadOnly ? 'Downloaded' : 'Installed') }) } } 'PowerShell 7' { if ($PSCmdlet.ShouldProcess('PowerShell 7', 'Install')) { Install-MicrosoftPwsh $results.Add([pscustomobject]@{ Component = 'PowerShell 7' Status = 'Installed' }) } } 'Visual Studio Code' { if ($PSCmdlet.ShouldProcess('Visual Studio Code', 'Install')) { Install-MicrosoftVSCode $results.Add([pscustomobject]@{ Component = 'Visual Studio Code' Status = 'Installed' }) } } 'Visual Studio Code Insiders' { if ($PSCmdlet.ShouldProcess('Visual Studio Code Insiders', 'Install')) { Install-MicrosoftVSCodeInsiders $results.Add([pscustomobject]@{ Component = 'Visual Studio Code Insiders' Status = 'Installed' }) } } 'Git for Windows' { if ($PSCmdlet.ShouldProcess('Git for Windows', 'Install')) { Install-GitForWindows $results.Add([pscustomobject]@{ Component = 'Git for Windows' Status = 'Installed' }) } } 'Hyper-V' { if ($PSCmdlet.ShouldProcess('Hyper-V', 'Install')) { $hvResult = Install-MicrosoftHyperV $results.Add([pscustomobject]@{ Component = 'Hyper-V' Status = 'Installed' RestartNeeded = ($hvResult.RestartNeeded -eq $true) }) } } '7-Zip' { if ($PSCmdlet.ShouldProcess('7-Zip', ($DownloadOnly ? 'Download' : 'Install'))) { Install-7Zip -DownloadOnly:$DownloadOnly $results.Add([pscustomobject]@{ Component = '7-Zip' Status = ($DownloadOnly ? 'Downloaded' : 'Installed') }) } } } } $results } |