quickstarts/microsoft.devcenter/devbox-ready-to-code-image/tools/artifacts/Windows-VisualStudio-Bootstrapper/Windows-VisualStudio-Bootstrapper.ps1 (133 lines of code) (raw):

[CmdletBinding()] param( [string] $WorkLoads, [String] $Sku, [String] $VSBootstrapperURL, [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [String] $InstallationDirectory, [bool] $SkipNgenAfterInstall = $false ) function Configure-WorkLoads { [CmdletBinding()] param( [string] $WorkLoads ) switch ($WorkLoads) { 'all' { $WorkLoads = '--all --includeRecommended --includeOptional' } 'minimal' { $WorkLoads = '--add Microsoft.VisualStudio.Workload.CoreEditor --add Microsoft.VisualStudio.Workload.ManagedDesktop --add Microsoft.VisualStudio.Workload.NativeCrossPlat --add Microsoft.VisualStudio.Workload.NativeDesktop --add Microsoft.VisualStudio.Workload.NetWeb --add Microsoft.VisualStudio.Workload.Office --add Microsoft.VisualStudio.Workload.Universal --add Microsoft.VisualStudio.Workload.VisualStudioExtension --includeRecommended --includeOptional' } 'reduced' { $WorkLoads = '--add Microsoft.VisualStudio.Workload.CoreEditor --add Microsoft.VisualStudio.Workload.ManagedDesktop --add Microsoft.VisualStudio.Workload.NativeCrossPlat --add Microsoft.VisualStudio.Workload.NativeDesktop --add Microsoft.VisualStudio.Workload.NetWeb --add Microsoft.VisualStudio.Workload.Office --add Microsoft.VisualStudio.Workload.Universal --add Microsoft.VisualStudio.Workload.VisualStudioExtension --add Microsoft.VisualStudio.Workload.Webcrossplat --includeRecommended --includeOptional' } 'coreeditor' { $WorkLoads = '--add Microsoft.VisualStudio.Workload.CoreEditor' } default { $WorkLoads = $WorkLoads } } return $WorkLoads } $ErrorActionPreference = 'Stop' function Run-WindowedApplication { param( [Parameter(Position = 0)][String]$command, [int[]]$AllowableExitStatuses = @(0), [int[]]$RetryableExitStatuses = @(), [Parameter(ValueFromRemainingArguments = $true)][String[]]$arguments ) if (!$AllowableExitStatuses.Contains(0)) { $AllowableExitStatuses += 0 } $maxRetries = 10 $retry = 0 while ($retry -le $maxRetries) { $outLog = [System.Guid]::NewGuid().ToString("N") $errLog = [System.Guid]::NewGuid().ToString("N") $startArgs = @{ FilePath = $command PassThru = $true NoNewWindow = $true Wait = $true RedirectStandardOutput = $outLog RedirectStandardError = $errLog } if ($arguments) { $startArgs["ArgumentList"] = $arguments } $proc = Start-Process @startArgs if (Test-Path $outLog) { Get-Content $outLog | Out-Host Remove-Item $outLog -ErrorAction SilentlyContinue } if (Test-Path $errLog) { Get-Content $errLog | Out-Host Remove-Item $errLog -ErrorAction SilentlyContinue } if ($RetryableExitStatuses.Contains($proc.ExitCode)) { Write-Host "Retry-able exit code spotted: $($proc.ExitCode)" Start-Sleep -Seconds 10 $retry += 1 Write-Host "Retry $retry/$maxRetries" continue } if (!$AllowableExitStatuses.Contains($proc.ExitCode)) { $errorlogs = Get-ChildItem -Path $env:TEMP | Where-Object { $_.Name -like "*dd_setup*" -and $_.Name -like "*_errors*" } $bootstrapperErrorLogs = Get-ChildItem -Path $env:TEMP | Where-Object { $_.Name -like "*dd_boot*" } foreach ($errorlog in $errorlogs) { Get-Content -Path $errorlog.FullName | Write-Output } foreach ($bootstrapperErrorLog in $bootstrapperErrorLogs) { Get-Content -Path $bootstrapperErrorLog.FullName | Write-Output } throw "Commmand exit code: $($proc.ExitCode) - $command $arguments" } return } } $randomBootStrapperName = -join ((65..90) + (97..122) | Get-Random -Count 5 | ForEach-Object { [char]$_ }) $vsSetupPath = "$env:Temp\$randomBootStrapperName.exe" [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 Invoke-WebRequest -Uri $VSBootstrapperURL -OutFile $vsSetupPath Write-Host "downloading $sku bootstrapper complete" Write-Host "Configuring workloads" $WorkLoads = Configure-WorkLoads -WorkLoads $WorkLoads Write-Host $WorkLoads if ($WorkLoads -eq "") { $Arguments = ('--quiet', '--norestart', '--wait' ) } else { $Arguments = ($WorkLoads, '--quiet', '--norestart', '--wait' ) } if (![System.String]::IsNullOrWhiteSpace($InstallationDirectory)) { Write-Host "Installing To: $InstallationDirectory" $Arguments += "--installPath ""$InstallationDirectory""" } Run-WindowedApplication -AllowableExitStatuses @(0, 3010) -RetryableExitStatuses 1618 $vsSetupPath $Arguments $item = Get-ChildItem 'C:\ProgramData\Microsoft\Windows\Start Menu\Programs\' -ErrorAction SilentlyContinue | Where-Object { $_.Name -like "Visual Studio 20*" -and $_.Attributes -like '*Archive*' } if ($null -ne $item) { Copy-Item $item.FullName -Destination 'C:\Users\Public\Desktop' -Force -ErrorAction SilentlyContinue } if (!$SkipNgenAfterInstall) { # run ngen on the installed assemblies $timestamp = Get-Date -Format "yyyyMMddHHmmss" @('Framework', 'Framework64') | ForEach-Object { try { $outFile = Join-Path $env:TEMP "ngen-$timestamp-$_.log" $errFile = Join-Path $env:TEMP "ngen-$timestamp-$_.err" $command = "C:\Windows\Microsoft.NET\$_\v4.0.30319\ngen.exe" $options = $('eqi') $cmdLine = "$command $($options -join ' ')" Write-Host "Running $cmdLine" Start-Process -FilePath $command -ArgumentList $options -Wait -NoNewWindow -RedirectStandardOutput $outFile -RedirectStandardError $errFile Write-Host "Running $cmdLine completed" if (0 -ne $LASTEXITCODE) { Write-Host "Running $cmdLine completed with exit code $LASTEXITCODE" } } catch { # ignore - some errors are expected Write-Host "Running $cmdLine completed with error" } } }