vs2022
Architecture: any
Architecture: any
name: vs2022
variables:
- name: vs_edition
value: "Community"
required: false
- name: vs_iso_path
value: ""
required: false
- name: vs_workloads
value: "Microsoft.VisualStudio.Workload.ManagedDesktop;Microsoft.VisualStudio.Workload.NetWeb"
required: false
- name: vs_components
value: ""
required: false
- name: vs_languages
value: "en-US"
required: false
- name: vs_install_path
value: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community"
required: false
- name: include_recommended
value: "true"
required: false
- name: include_optional
value: "false"
required: false
- name: dev_drive_letter
value: ""
required: false
fodder:
- name: install-vs-2022
type: shellscript
filename: install-vs.ps1
content: |
Write-Host "Starting Visual Studio $edition 2022 installation process..."
# Function to test if a command exists
function Test-CommandExists {
param($Command)
try {
if (Get-Command $Command -ErrorAction Stop) {
return $true
}
} catch {
return $false
}
return $false
}
# Function to detect VS installer on mounted ISO
function Find-VSInstallerOnISO {
param([string]$Edition)
Write-Host "Searching for Visual Studio $Edition installer on mounted drives..."
# Skip D: drive as it contains cloud-init disk
$drivesToCheck = @('E:', 'F:', 'G:', 'H:')
foreach ($drive in $drivesToCheck) {
if (Test-Path $drive) {
Write-Host "Checking drive $drive..."
# Common VS installer locations on ISO for different editions
$installerPaths = @(
"$drive\vs_$($Edition.ToLower()).exe",
"$drive\vs_community.exe",
"$drive\vs_professional.exe",
"$drive\vs_enterprise.exe",
"$drive\vs_setup.exe",
"$drive\setup.exe",
"$drive\vs_installer\vs_$($Edition.ToLower()).exe"
)
foreach ($path in $installerPaths) {
if (Test-Path $path) {
Write-Host "Found VS installer at: $path"
return $path
}
}
}
}
return $null
}
# Function to mount ISO if path provided
function Mount-VsISO {
param($IsoPath)
if ([string]::IsNullOrWhiteSpace($IsoPath)) {
return $null
}
if (!(Test-Path $IsoPath)) {
Write-Host "WARNING: Provided ISO path does not exist: $IsoPath"
return $null
}
Write-Host "Mounting ISO from: $IsoPath"
try {
$mount = Mount-DiskImage -ImagePath $IsoPath -PassThru
$driveLetter = ($mount | Get-Volume).DriveLetter
if ($driveLetter) {
$installerPath = "${driveLetter}:\vs_$($Edition.ToLower()).exe"
if (!(Test-Path $installerPath)) {
$installerPath = "${driveLetter}:\vs_setup.exe"
}
if (!(Test-Path $installerPath)) {
$installerPath = "${driveLetter}:\setup.exe"
}
if (Test-Path $installerPath) {
Write-Host "Found installer at: $installerPath"
return $installerPath
} else {
Write-Host "No VS installer found on mounted ISO"
Dismount-DiskImage -ImagePath $IsoPath
return $null
}
}
} catch {
Write-Host "Error mounting ISO: $_"
return $null
}
return $null
}
# Function to download VS bootstrapper
function Download-VSBootstrapper {
param([string]$Edition)
Write-Host "Downloading Visual Studio $Edition 2022 bootstrapper..."
# Map edition to bootstrapper URL
$editionUrls = @{
"Community" = "https://aka.ms/vs/17/release/vs_community.exe"
"Professional" = "https://aka.ms/vs/17/release/vs_professional.exe"
"Enterprise" = "https://aka.ms/vs/17/release/vs_enterprise.exe"
}
if (-not $editionUrls.ContainsKey($Edition)) {
Write-Host "ERROR: Unknown edition '$Edition'. Valid editions: Community, Professional, Enterprise"
return $null
}
$bootstrapperUrl = $editionUrls[$Edition]
$bootstrapperPath = "$env:TEMP\vs_$($Edition.ToLower()).exe"
try {
Invoke-WebRequest -Uri $bootstrapperUrl -OutFile $bootstrapperPath -UseBasicParsing
if (Test-Path $bootstrapperPath) {
Write-Host "Bootstrapper downloaded to: $bootstrapperPath"
return $bootstrapperPath
}
} catch {
Write-Host "Error downloading bootstrapper: $_"
return $null
}
return $null
}
# Function to build installation arguments
function Build-InstallArguments {
param(
[string]$InstallPath,
[string]$Workloads,
[string]$Components,
[string]$Languages,
[bool]$IncludeRecommended,
[bool]$IncludeOptional
)
$args = @(
"--quiet",
"--wait",
"--norestart",
"--installPath", "`"$InstallPath`""
)
# Add workloads
if (![string]::IsNullOrWhiteSpace($Workloads)) {
$workloadList = $Workloads -split ';'
foreach ($workload in $workloadList) {
$workload = $workload.Trim()
if (![string]::IsNullOrWhiteSpace($workload)) {
$args += "--add", $workload
}
}
}
# Add individual components
if (![string]::IsNullOrWhiteSpace($Components)) {
$componentList = $Components -split ';'
foreach ($component in $componentList) {
$component = $component.Trim()
if (![string]::IsNullOrWhiteSpace($component)) {
$args += "--add", $component
}
}
}
# Add language packs
if (![string]::IsNullOrWhiteSpace($Languages)) {
$languageList = $Languages -split ';'
foreach ($language in $languageList) {
$language = $language.Trim()
if (![string]::IsNullOrWhiteSpace($language)) {
$args += "--addProductLang", $language
}
}
}
# Include recommended components
if ($IncludeRecommended) {
$args += "--includeRecommended"
}
# Include optional components
if ($IncludeOptional) {
$args += "--includeOptional"
}
return $args
}
# Main installation logic
$installed = $false
$installerPath = $null
$edition = "{{ vs_edition }}"
$isoPath = "{{ vs_iso_path }}"
$workloads = "{{ vs_workloads }}"
$components = "{{ vs_components }}"
$languages = "{{ vs_languages }}"
$installPath = "{{ vs_install_path }}"
$includeRecommended = [bool]::Parse("{{ include_recommended }}")
$includeOptional = [bool]::Parse("{{ include_optional }}")
# Adjust install path based on edition if using default path
if ($installPath -like "*\Community") {
$installPath = $installPath -replace "Community", $edition
}
# Check if VS is already installed
if (Test-Path "$installPath\Common7\IDE\devenv.exe") {
Write-Host "Visual Studio is already installed at: $installPath"
$installed = $true
}
if (!$installed) {
# Try to find installer in this order:
# 1. User-provided ISO path
# 2. Auto-detect mounted ISO
# 3. Download bootstrapper
if (![string]::IsNullOrWhiteSpace($isoPath)) {
Write-Host "Attempting to use provided ISO path..."
$installerPath = Mount-VsISO -IsoPath $isoPath
}
if (!$installerPath) {
Write-Host "Checking for auto-mounted ISO..."
$installerPath = Find-VSInstallerOnISO -Edition $edition
}
if (!$installerPath) {
Write-Host "No ISO found, downloading from internet..."
$installerPath = Download-VSBootstrapper -Edition $edition
}
if (!$installerPath) {
Write-Host "ERROR: Could not obtain Visual Studio installer"
exit 1
}
# Build installation arguments
$installArgs = Build-InstallArguments -InstallPath $installPath -Workloads $workloads -Components $components -Languages $languages -IncludeRecommended $includeRecommended -IncludeOptional $includeOptional
Write-Host "Starting Visual Studio installation..."
Write-Host "Installer: $installerPath"
Write-Host "Arguments: $($installArgs -join ' ')"
# Create log directory
$logDir = "$env:TEMP\VSInstallLogs"
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
# Execute installation
try {
$process = Start-Process -FilePath $installerPath -ArgumentList $installArgs -Wait -PassThru -NoNewWindow
$exitCode = $process.ExitCode
if ($exitCode -eq 0) {
Write-Host "Visual Studio installation completed successfully"
$installed = $true
} elseif ($exitCode -eq 3010) {
Write-Host "Visual Studio installation completed successfully (reboot required)"
$installed = $true
# Exit with cloud-init code 1001 to request reboot and continue
Write-Host "Requesting system reboot to complete VS installation..."
exit 1001
} elseif ($exitCode -eq 1641) {
Write-Host "Visual Studio installer initiated a restart"
# Exit with cloud-init code 1003 to reboot and retry
exit 1003
} else {
Write-Host "Visual Studio installation failed with exit code: $exitCode"
Write-Host "Check logs in: $logDir"
# Try to find and display error logs
$errorLog = Get-ChildItem -Path "$env:TEMP" -Filter "dd_*.log" -ErrorAction SilentlyContinue |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1
if ($errorLog) {
Write-Host "Recent log file: $($errorLog.FullName)"
Get-Content $errorLog.FullName -Tail 50 | Write-Host
}
}
} catch {
Write-Host "Error during installation: $_"
}
# Clean up downloaded bootstrapper if used
if ($installerPath -like "*\Temp\*") {
Remove-Item $installerPath -Force -ErrorAction SilentlyContinue
}
# Unmount ISO if we mounted it
if (![string]::IsNullOrWhiteSpace($isoPath) -and (Test-Path $isoPath)) {
try {
Dismount-DiskImage -ImagePath $isoPath -ErrorAction SilentlyContinue
} catch {
# Ignore dismount errors
}
}
}
# Final verification
if ($installed) {
Write-Host "Visual Studio $edition 2022 installation completed!"
# Add VS tools to PATH
$vsPath = "$installPath\Common7\IDE"
if (Test-Path $vsPath) {
$currentPath = [System.Environment]::GetEnvironmentVariable("Path", "Machine")
if ($currentPath -notlike "*$vsPath*") {
[System.Environment]::SetEnvironmentVariable("Path", "$currentPath;$vsPath", "Machine")
Write-Host "Added Visual Studio to system PATH"
}
}
# Display installed workloads
Write-Host "`nInstalled with workloads:"
if (![string]::IsNullOrWhiteSpace($workloads)) {
$workloads -split ';' | ForEach-Object { Write-Host " - $_" }
} else {
Write-Host " - Default workloads"
}
# Detect and configure Dev Drive for Visual Studio
Write-Host "`n=== Configuring Dev Drive for Visual Studio ==="
$providedDevDrive = "{{ dev_drive_letter }}"
$devDriveLetter = $null
# Check if dev_drive_letter was explicitly provided
if (![string]::IsNullOrWhiteSpace($providedDevDrive)) {
Write-Host "Using explicitly provided Dev Drive letter: ${providedDevDrive}:"
# Verify the drive exists and is suitable
$volume = Get-Volume -DriveLetter $providedDevDrive -ErrorAction SilentlyContinue
if ($volume) {
if ($volume.DriveType -eq 'Fixed') {
$devDriveLetter = $providedDevDrive
Write-Host " File System: $($volume.FileSystem)"
Write-Host " Label: $($volume.FileSystemLabel)"
Write-Host " Size: $([Math]::Round($volume.Size / 1GB, 2)) GB"
} else {
Write-Host "WARNING: Drive ${providedDevDrive}: is not a fixed drive. Skipping Dev Drive configuration."
}
} else {
Write-Host "WARNING: Drive ${providedDevDrive}: does not exist. Skipping Dev Drive configuration."
}
} else {
# Auto-detect Dev Drive
Write-Host "Auto-detecting Dev Drive..."
$volumes = Get-Volume | Where-Object { $_.DriveType -eq 'Fixed' -and $_.DriveLetter }
foreach ($volume in $volumes) {
$driveLetter = $volume.DriveLetter
# Check if it's a Dev Drive (Windows 11)
$devDrvResult = cmd /c "fsutil devdrv query ${driveLetter}:" 2>&1
if ($devDrvResult -like "*trusted developer volume*") {
Write-Host "Found Dev Drive at ${driveLetter}:"
$devDriveLetter = $driveLetter
break
}
# Check for ReFS drives labeled as DevDrive
elseif ($volume.FileSystem -eq 'ReFS' -and $volume.FileSystemLabel -like '*Dev*') {
Write-Host "Found ReFS development drive at ${driveLetter}:"
$devDriveLetter = $driveLetter
break
}
}
}
if ($devDriveLetter) {
Write-Host "Configuring Visual Studio for Dev Drive at ${devDriveLetter}:"
# Create VS project directories
$vsPaths = @(
"${devDriveLetter}:\source\repos",
"${devDriveLetter}:\source\projects",
"${devDriveLetter}:\source\templates\ProjectTemplates",
"${devDriveLetter}:\source\templates\ItemTemplates",
"${devDriveLetter}:\SymbolCache"
)
foreach ($path in $vsPaths) {
if (-not (Test-Path $path)) {
New-Item -ItemType Directory -Path $path -Force | Out-Null
Write-Host "Created: $path"
}
}
# Create user configuration script
$configScript = @"
Write-Host 'Configuring Visual Studio 2022 for Dev Drive...'
Write-Host ''
Write-Host 'Please configure the following in Visual Studio:'
Write-Host '1. Go to Tools -> Options -> Projects and Solutions -> Locations'
Write-Host ' - Projects location: ${devDriveLetter}:\source\repos'
Write-Host ' - User project templates: ${devDriveLetter}:\source\templates\ProjectTemplates'
Write-Host ' - User item templates: ${devDriveLetter}:\source\templates\ItemTemplates'
Write-Host ''
Write-Host '2. Go to Tools -> Options -> Debugging -> Symbols'
Write-Host ' - Symbol cache directory: ${devDriveLetter}:\SymbolCache'
Write-Host ''
Write-Host 'Dev Drive provides ~25% better performance for builds and file operations.'
"@
$configScript | Out-File -FilePath "C:\ProgramData\configure-vs-devdrive.ps1" -Encoding UTF8 -Force
Write-Host "Configuration script saved to: C:\ProgramData\configure-vs-devdrive.ps1"
Write-Host "Run this script for each user to configure their VS settings."
} else {
Write-Host "No Dev Drive detected. Visual Studio will use default locations."
Write-Host "Consider creating a Dev Drive for better performance."
}
} else {
Write-Host "ERROR: Failed to install Visual Studio $edition 2022"
exit 1
}