Artifacts/windows-visualstudio/artifact.ps1 (253 lines of code) (raw):

Param( [Parameter(Mandatory=$true)] [ValidateSet("2015","2017","2019","2022")] [string] $version, [Parameter(Mandatory=$true)] [ValidateSet("Professional","Enterprise")] [string] $sku, [Parameter()] [string] $installerArgs ) ################################################################################################### # # PowerShell configurations # # NOTE: Because the $ErrorActionPreference is "Stop", this script will stop on first failure. # This is necessary to ensure we capture errors inside the try-catch-finally block. $ErrorActionPreference = "Stop" # Hide any progress bars, due to downloads and installs of remote components. $ProgressPreference = "SilentlyContinue" # Ensure we force use of TLS 1.2 for all downloads. [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 # Discard any collected errors from a previous execution. $Error.Clear() ################################################################################################### # # Handle all errors in this script. # trap { # NOTE: This trap will handle all errors. There should be no need to use a catch below in this # script, unless you want to ignore a specific error. $message = $Error[0].Exception.Message if ($message) { Write-Host -Object "`nERROR: $message" -ForegroundColor Red } Write-Host "`nThe artifact failed to apply.`n" # IMPORTANT NOTE: Throwing a terminating error (using $ErrorActionPreference = "Stop") still # returns exit code zero from the PowerShell script when using -File. The workaround is to # NOT use -File when calling this script and leverage the try-catch-finally block and return # a non-zero exit code from the catch block. exit -1 } ################################################################################################### # # Functions used in this script. # function DownloadToFilePath ($downloadUrl, $targetFile) { Write-Output ("Downloading installation files from URL: $downloadUrl to $targetFile") $targetFolder = Split-Path $targetFile if ((Test-Path -path $targetFile)) { Write-Output "Deleting old target file $targetFile" Remove-Item $targetFile -Force | Out-Null } if (-not (Test-Path -path $targetFolder)) { Write-Output "Creating folder $targetFolder" New-Item -ItemType Directory -Force -Path $targetFolder | Out-Null } # Download the file, with retries. $downloadAttempts = 0 do { $downloadAttempts++ try { $WebClient = New-Object System.Net.WebClient $WebClient.DownloadFile($downloadUrl,$targetFile) break } catch { Write-Output "Caught exception during download..." if ($_.Exception.InnerException) { Write-Output "InnerException: $($_.InnerException.Message)" } else { Write-Output "Exception: $($_.Exception.Message)" } } } while ($downloadAttempts -lt 5) if ($downloadAttempts -eq 5) { Write-Error "Download of $downloadUrl failed repeatedly. Giving up." } } ################################################################################################### # # Main execution block. # try { Push-Location $PSScriptRoot Write-Output "Installing Visual Studio $version $sku" $logFolder = Join-path -path $env:ProgramData -childPath "DTLArt_VS" # Split the given installer arguments accounting for flags (i.e./Quiet), arguments with values (i.e. /Log $vsLog), # arguments with dashes (i.e. -Quiet) or even double dashes (i.e. --quiet), etc. [array]$installerArgsList = $installerArgs -split '(?<![\w|-])(?=\-)|(?<!\w)(?=/)' | ? { $_ -ne '' } | % { $_.trim() } if ($installerArgsList) { Write-Output "InstallerArgs value: $installerArgsList" } if ($version -eq '2015' ) { $vsLog = Join-Path $logFolder "VSInstall.log" $argumentList = $installerArgsList + @( "/Quiet", "/NoRestart" "/Log $vsLog" ) if ($sku -eq 'Professional') { $downloadUrl = 'http://go.microsoft.com/fwlink/?LinkId=615435' } elseif ($sku -eq 'Enterprise') { $downloadUrl = 'http://go.microsoft.com/fwlink/?LinkId=615437' } } elseif ($version -eq '2017') { $commonArgsList = @( "--includeRecommended", "--quiet", "--norestart", "--wait" ) # Proffesional $proffesionalModulesArgsList = @( "--add Microsoft.VisualStudio.Workload.Azure", "--add Microsoft.VisualStudio.Workload.Data", "--add Microsoft.VisualStudio.Workload.ManagedDesktop", "--add Microsoft.VisualStudio.Workload.NativeDesktop", "--add Microsoft.VisualStudio.Workload.NetCoreTools", "--add Microsoft.VisualStudio.Workload.NetWeb" ) # Enterprise (includes all proffesional modules) $enterpriseModulesArgsList = $proffesionalModulesArgsList + @( "--add Component.GitHub.VisualStudio", "--add Microsoft.VisualStudio.Component.TestTools.MicrosoftTestManager", "--add Microsoft.VisualStudio.Component.TestTools.WebLoadTest", "--add Microsoft.VisualStudio.Workload.NativeCrossPlat" ) if ($sku -eq 'Professional') { $argumentList = $installerArgsList + $commonArgsList + $proffesionalModulesArgsList $downloadUrl = 'https://download.visualstudio.microsoft.com/download/pr/100196700/14dd70405e8244481b35017b9a562edd/vs_Professional.exe' } elseif ($sku -eq 'Enterprise') { $argumentList = $installerArgsList + $commonArgsList + $enterpriseModulesArgsList $downloadUrl = 'https://download.microsoft.com/download/F/3/4/F3478590-7B38-48B1-BB6E-3141A9A155E7/vs_Enterprise.exe' } } elseif ($version -eq '2019') { $commonArgsList = @( "--includeRecommended", "--quiet", "--norestart", "--wait" ) # Proffesional $proffesionalModulesArgsList = @( "--add Microsoft.VisualStudio.Workload.Azure", "--add Microsoft.VisualStudio.Workload.Data", "--add Microsoft.VisualStudio.Workload.ManagedDesktop", "--add Microsoft.VisualStudio.Workload.NativeDesktop", "--add Microsoft.VisualStudio.Workload.NetCoreTools", "--add Microsoft.VisualStudio.Workload.NetWeb" ) # Enterprise (includes all proffesional modules) $enterpriseModulesArgsList = $proffesionalModulesArgsList + @( "--add Component.GitHub.VisualStudio", "--add Microsoft.VisualStudio.Component.TestTools.WebLoadTest", "--add Microsoft.VisualStudio.Workload.NativeCrossPlat" ) if ($sku -eq 'Professional') { $argumentList = $installerArgsList + $commonArgsList + $proffesionalModulesArgsList $downloadUrl = 'https://aka.ms/vs/16/release/vs_professional.exe' } elseif ($sku -eq 'Enterprise') { $argumentList = $installerArgsList + $commonArgsList + $enterpriseModulesArgsList $downloadUrl = 'https://aka.ms/vs/16/release/vs_enterprise.exe' } } elseif ($version -eq '2022') { $commonArgsList = @( "--includeRecommended", "--quiet", "--norestart", "--wait" ) # Proffesional $proffesionalModulesArgsList = @( "--add Microsoft.VisualStudio.Workload.Azure", "--add Microsoft.VisualStudio.Workload.Data", "--add Microsoft.VisualStudio.Workload.ManagedDesktop", "--add Microsoft.VisualStudio.Workload.NativeDesktop", "--add Microsoft.VisualStudio.Workload.NetWeb" ) # Enterprise (includes all proffesional modules) $enterpriseModulesArgsList = $proffesionalModulesArgsList + @( "--add Component.GitHub.VisualStudio", "--add Microsoft.VisualStudio.Component.TestTools.WebLoadTest", "--add Microsoft.VisualStudio.Workload.NativeCrossPlat" ) if ($sku -eq 'Professional') { $argumentList = $installerArgsList + $commonArgsList + $proffesionalModulesArgsList $downloadUrl = 'https://aka.ms/vs/17/release/vs_professional.exe' } elseif ($sku -eq 'Enterprise') { $argumentList = $installerArgsList + $commonArgsList + $enterpriseModulesArgsList $downloadUrl = 'https://aka.ms/vs/17/release/vs_enterprise.exe' } } else { throw "Version is not recognized - allowed values are 2015, 2017, 2019, and 2022. Specified value: $version" } $localFile = Join-Path $logFolder 'vsinstaller.exe' DownloadToFilePath $downloadUrl $localFile # Ensure there are no duplicate entries in the argument list. $argumentList = $argumentList | Select -Unique Write-Output "Running install with the following arguments: $argumentList" $retCode = Start-Process -FilePath $localFile -ArgumentList $argumentList -Wait -PassThru if ($retCode.ExitCode -ne 0 -and $retCode.ExitCode -ne 3010) { if($version -eq '2017') { $targetLogs = 'c:\VS2017Logs' New-Item -ItemType Directory -Force -Path $targetLogs | Out-Null Write-Output ('Temp location is ' + $env:TEMP) Copy-Item -path $env:TEMP\dd* -Destination $targetLogs } throw "Product installation of $localFile failed with exit code: $($retCode.ExitCode.ToString())" } Write-Output "Visual Studio install succeeded. Rebooting..." Write-Host "`nThe artifact was applied successfully.`n" } finally { Pop-Location }