diff --git a/.configuration/devcenter/tasks/choco/Choco.ps1 b/.configuration/devcenter/tasks/choco/Choco.ps1 deleted file mode 100644 index c10aba8f..00000000 --- a/.configuration/devcenter/tasks/choco/Choco.ps1 +++ /dev/null @@ -1,151 +0,0 @@ -[CmdletBinding()] -param( - [Parameter(Mandatory=$true)] - [string] $Package, - - [Parameter()] - [string] $Version, - - [Parameter()] - [string] $Switches, - - [Parameter()] - [string] $IgnoreChecksums -) - -if (-not $Package) { - throw "Package parameter is mandatory. Please provide a value for the Package parameter." -} - -################################################################################################### -# -# PowerShell configurations -# - -# Ensure we force use of TLS 1.2 for all downloads. -[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - -# Expected path of the choco.exe file. -$Choco = "$Env:ProgramData/chocolatey/choco.exe" - -################################################################################################### -# -# Functions used in this script. -# - -function Ensure-Chocolatey -{ - [CmdletBinding()] - param( - [string] $ChocoExePath - ) - - if (-not (Test-Path "$ChocoExePath")) - { - Set-ExecutionPolicy Bypass -Scope Process -Force - $installScriptPath = [System.IO.Path]::GetTempFileName() + ".ps1" - Invoke-WebRequest -Uri 'https://chocolatey.org/install.ps1' -OutFile $installScriptPath - - try { - Execute -File $installScriptPath - } finally { - Remove-Item $installScriptPath - } - - if ($LastExitCode -eq 3010) - { - Write-Host 'The recent changes indicate a reboot is necessary. Please reboot at your earliest convenience.' - } - } -} - -function Install-Package -{ - [CmdletBinding()] - param( - [string] $ChocoExePath, - [string] $Package, - [string] $Version, - [string] $Switches, - [string] $IgnoreChecksums - ) - - $expression = "$ChocoExePath install $Package" - - if ($Version){ - $expression = "$expression --version $Version" - } - - if ($Switches){ - $expression = "$expression --params ""'$Switches'"" " - } - - $expression = "$expression -y -f --acceptlicense --no-progress --stoponfirstfailure" - - if ($IgnoreChecksums -eq "true") { - $expression = "$expression --ignorechecksums" - } - - $expression = "$expression `nexit `$LASTEXITCODE" - - Set-ExecutionPolicy Bypass -Scope Process -Force - $packageScriptPath = [System.IO.Path]::GetTempFileName() + ".ps1" - Set-Content -Value $expression -Path $packageScriptPath - Write-Host "File path $packageScriptPath" - - Execute -File $packageScriptPath - Remove-Item $packageScriptPath -} - -function Execute -{ - [CmdletBinding()] - param( - $File - ) - - # Note we're calling powershell.exe directly, instead - # of running Invoke-Expression, as suggested by - # https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/avoid-using-invoke-expression?view=powershell-7.3 - # Note that this will run powershell.exe - # even if the system has pwsh.exe. - powershell.exe -File $File - - # capture the exit code from the process - $processExitCode = $LASTEXITCODE - - # This check allows us to capture cases where the command we execute exits with an error code. - # In that case, we do want to throw an exception with whatever is in stderr. Normally, when - # Invoke-Expression throws, the error will come the normal way (i.e. $Error) and pass via the - # catch below. - if ($processExitCode -or $expError) - { - if ($processExitCode -eq 3010) - { - # Expected condition. The recent changes indicate a reboot is necessary. Please reboot at your earliest convenience. - } - elseif ($expError) - { - throw $expError - } - else - { - throw "Installation failed with exit code: $processExitCode. Please see the Chocolatey logs in %ALLUSERSPROFILE%\chocolatey\logs folder for details." - break - } - } -} - -################################################################################################### -# -# Main execution block. -# - -Write-Host 'Ensuring latest Chocolatey version is installed.' -Ensure-Chocolatey -ChocoExePath "$Choco" - -Write-Host "Preparing to install Chocolatey package: $Package." -Write-Host "Command: $Choco -Package $Package -Version $Version -Switches $Switches -IgnoreChecksums $IgnoreChecksums" -Install-Package -ChocoExePath "$Choco" -Package $Package -Version $Version -Switches $Switches -IgnoreChecksums $IgnoreChecksums - -Write-Host "`nThe artifact was applied successfully.`n" diff --git a/.configuration/devcenter/tasks/choco/task.yaml b/.configuration/devcenter/tasks/choco/task.yaml deleted file mode 100644 index 39b8e55b..00000000 --- a/.configuration/devcenter/tasks/choco/task.yaml +++ /dev/null @@ -1,49 +0,0 @@ - -# This is a Chocolatey package installation task for Dev Box. -$schema: "1.0" -name: choco -description: Installs a Chocolatey package. -author: Microsoft Corporation -command: "./Choco.ps1 -Package {{package}} -Version {{version}} -Switches {{switches}} -IgnoreChecksums {{ignoreChecksums}}" -parameters: - package: - default: "" - type: string - required: true - description: | - The name of the Chocolatey package to install. - For example, "git". - Visit https://chocolatey.org/packages to learn - more about Chocolatey packages. - version: - default: "" - type: string - required: false - description: The version of the Chocolatey package to install. - switches: - default: "" - type: string - required: false - description: Additional params required for the package to install. - ignoreChecksums: - default: false - type: boolean - required: false - description: Whether to ignore checksums when installing the package. -documentation: - notes: This task is used to install a Chocolatey package. - examples: - - name: choco - description: install OracleJDK 17.0.2 - parameters: - package: oraclejdk - version: 17.0.2 - - name: choco - description: install azure functions core tools - parameters: - package: azure-functions-core-tools - switches: /x64 - - name: choco - description: install notepad++ - parameters: - package: notepadplusplus diff --git a/.configuration/devcenter/tasks/git-clone/cleanup.ps1 b/.configuration/devcenter/tasks/git-clone/cleanup.ps1 deleted file mode 100644 index 9132e24c..00000000 --- a/.configuration/devcenter/tasks/git-clone/cleanup.ps1 +++ /dev/null @@ -1,12 +0,0 @@ -$CustomizationScriptsDir = "C:\DevBoxCustomizations" -$LockFile = "lockfile" -$RunAsUserScript = "runAsUser.ps1" -$CleanupScript = "cleanup.ps1" -$RunAsUserTask = "DevBoxCustomizations" -$CleanupTask = "DevBoxCustomizationsCleanup" - -if (!(Test-Path "$($CustomizationScriptsDir)\$($LockFile)")) { - Unregister-ScheduledTask -TaskName $RunAsUserTask -Confirm:$false - Unregister-ScheduledTask -TaskName $CleanupTask -Confirm:$false - Remove-Item $CustomizationScriptsDir -Force -Recurse -} \ No newline at end of file diff --git a/.configuration/devcenter/tasks/git-clone/main.ps1 b/.configuration/devcenter/tasks/git-clone/main.ps1 deleted file mode 100644 index 5c8d0cd7..00000000 --- a/.configuration/devcenter/tasks/git-clone/main.ps1 +++ /dev/null @@ -1,620 +0,0 @@ -param ( - [Parameter()] - [string]$RepositoryUrl, - [Parameter()] - [string]$Directory, - [Parameter()] - [string]$Branch, - [Parameter()] - [string]$Pat -) - - -$CustomizationScriptsDir = "C:\DevBoxCustomizations" -$LockFile = "lockfile" -$RunAsUserScript = "runAsUser.ps1" -$CleanupScript = "cleanup.ps1" -$RunAsUserTask = "DevBoxCustomizations" -$CleanupTask = "DevBoxCustomizationsCleanup" -$PsInstallScope = "CurrentUser" -if ($(whoami.exe) -eq "nt authority\system") { - $PsInstallScope = "AllUsers" -} -$TargetRepoDirectory = Join-Path -Path $Directory -ChildPath "$($RepositoryUrl -replace "^.+\/([^\/]+?)(?:\.git)?$", '$1')" - -# Set the progress preference to silently continue -# in order to avoid progress bars in the output -# as this makes web requests very slow -# Reference: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables -$ProgressPreference = 'SilentlyContinue' - -function SetupScheduledTasks { - Write-Host "Setting up scheduled tasks" - if (!(Test-Path -PathType Container $CustomizationScriptsDir)) { - New-Item -Path $CustomizationScriptsDir -ItemType Directory - New-Item -Path "$($CustomizationScriptsDir)\$($LockFile)" -ItemType File - Copy-Item "./$($RunAsUserScript)" -Destination $CustomizationScriptsDir - Copy-Item "./$($CleanupScript)" -Destination $CustomizationScriptsDir - } - - # Reference: https://learn.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-objects - $SchedService = New-Object -comobject "Schedule.Service" - $SchedService.Connect() - - # Schedule the cleanup script to run every minute as SYSTEM - $Task = $SchedService.NewTask(0) - $Task.RegistrationInfo.Description = "Dev Box Customizations Cleanup" - $Task.Settings.Enabled = $true - $Task.Settings.AllowDemandStart = $false - - $Trigger = $Task.Triggers.Create(9) - $Trigger.Enabled = $true - $Trigger.Repetition.Interval="PT1M" - - $Action = $Task.Actions.Create(0) - $Action.Path = "PowerShell.exe" - $Action.Arguments = "Set-ExecutionPolicy Bypass -Scope Process -Force; $($CustomizationScriptsDir)\$($CleanupScript)" - - $TaskFolder = $SchedService.GetFolder("\") - $TaskFolder.RegisterTaskDefinition("$($CleanupTask)", $Task , 6, "NT AUTHORITY\SYSTEM", $null, 5) - - # Schedule the script to be run in the user context on login - $Task = $SchedService.NewTask(0) - $Task.RegistrationInfo.Description = "Dev Box Customizations" - $Task.Settings.Enabled = $true - $Task.Settings.AllowDemandStart = $false - $Task.Principal.RunLevel = 1 - - $Trigger = $Task.Triggers.Create(9) - $Trigger.Enabled = $true - - $Action = $Task.Actions.Create(0) - $Action.Path = "C:\Program Files\PowerShell\7\pwsh.exe" - $Action.Arguments = "-MTA -Command $($CustomizationScriptsDir)\$($RunAsUserScript)" - - $TaskFolder = $SchedService.GetFolder("\") - $TaskFolder.RegisterTaskDefinition("$($RunAsUserTask)", $Task , 6, "Users", $null, 4) - Write-Host "Done setting up scheduled tasks" -} - -function WithRetry { - Param( - [Parameter(Position=0, Mandatory=$true)] - [scriptblock]$ScriptBlock, - - [Parameter(Position=1, Mandatory=$false)] - [int]$Maximum = 5, - - [Parameter(Position=2, Mandatory=$false)] - [int]$Delay = 100 - ) - - $iterationCount = 0 - $lastException = $null - do { - $iterationCount++ - try { - Invoke-Command -Command $ScriptBlock - return - } catch { - $lastException = $_ - Write-Error $_ - - # Sleep for a random amount of time with exponential backoff - $randomDouble = Get-Random -Minimum 0.0 -Maximum 1.0 - $k = $randomDouble * ([Math]::Pow(2.0, $iterationCount) - 1.0) - Start-Sleep -Milliseconds ($k * $Delay) - } - } while ($iterationCount -lt $Maximum) - - throw $lastException -} - -function InstallPS7 { - if (!(Get-Command pwsh -ErrorAction SilentlyContinue)) { - Write-Host "Installing PowerShell 7" - $code = Invoke-RestMethod -Uri https://aka.ms/install-powershell.ps1 - $null = New-Item -Path function:Install-PowerShell -Value $code - WithRetry -ScriptBlock { - if ("$($PsInstallScope)" -eq "CurrentUser") { - Install-PowerShell -UseMSI - } - else { - # The -Quiet flag requires admin permissions - Install-PowerShell -UseMSI -Quiet - } - } -Maximum 5 -Delay 100 - # Need to update the path post install - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + ";C:\Program Files\PowerShell\7" - Write-Host "Done Installing PowerShell 7" - } - else { - Write-Host "PowerShell 7 is already installed" - } -} - -function InstallWinGet { - Write-Host "Installing powershell modules in scope: $PsInstallScope" - - # ensure NuGet provider is installed - if (!(Get-PackageProvider | Where-Object { $_.Name -eq "NuGet" -and $_.Version -gt "2.8.5.201" })) { - Write-Host "Installing NuGet provider" - Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope $PsInstallScope - Write-Host "Done Installing NuGet provider" - } - else { - Write-Host "NuGet provider is already installed" - } - - # Set PSGallery installation policy to trusted - Set-PSRepository -Name "PSGallery" -InstallationPolicy Trusted - pwsh.exe -MTA -Command "Set-PSRepository -Name PSGallery -InstallationPolicy Trusted" - - # check if the Microsoft.Winget.Client module is installed - $wingetClientPackage = pwsh.exe -Command "Get-Module -ListAvailable -Name Microsoft.WinGet.Client | Where-Object { `$_.Version -ge '1.9.2411' }" - if (!($wingetClientPackage)) { - Write-Host "Installing Microsoft.Winget.Client" - pwsh.exe -MTA -Command "Install-Module Microsoft.WinGet.Client -Scope $PsInstallScope" - Write-Host "Done Installing Microsoft.Winget.Client" - } - else { - Write-Host "Microsoft.Winget.Client is already installed" - } - - # check if the Microsoft.WinGet.Configuration module is installed - $wingetConfigurationPackage = pwsh.exe -Command "Get-Module -ListAvailable -Name Microsoft.WinGet.Configuration | Where-Object { `$_.Version -ge '1.8.1911' }" - if (!($wingetConfigurationPackage)) { - Write-Host "Installing Microsoft.WinGet.Configuration" - pwsh.exe -MTA -Command "Install-Module Microsoft.WinGet.Configuration -AllowPrerelease -Scope $PsInstallScope" - Write-Host "Done Installing Microsoft.WinGet.Configuration" - } - else { - Write-Host "Microsoft.WinGet.Configuration is already installed" - } - - Write-Host "Updating WinGet" - try { - Write-Host "Attempting to repair WinGet Package Manager" - pwsh.exe -MTA -Command "Repair-WinGetPackageManager -Latest -Force -Verbose" - Write-Host "Done Reparing WinGet Package Manager" - } - catch { - Write-Host "Failed to repair WinGet Package Manager" - Write-Error $_ - } - - if ($PsInstallScope -eq "CurrentUser") { - # Under a user account, the way to materialize winget.exe and make it work is by installing DesktopAppInstaller appx, - # which in turn may have Xaml and VC++ redistributable requirements. - - $architecture = "x64" - if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") { - $architecture = "arm64" - } - - $msVCLibsPackage = Get-AppxPackage -Name "Microsoft.VCLibs.140.00.UWPDesktop" | Where-Object { $_.Version -ge "14.0.30035.0" } - if (!($msVCLibsPackage)) { - # Install Microsoft.VCLibs.140.00.UWPDesktop - try { - Write-Host "Installing Microsoft.VCLibs.140.00.UWPDesktop" - $MsVCLibs = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-Microsoft.VCLibs.140.00.UWPDesktop" - $MsVCLibsAppx = "$($MsVCLibs).appx" - - Invoke-WebRequest -Uri "https://aka.ms/Microsoft.VCLibs.$($architecture).14.00.Desktop.appx" -OutFile $MsVCLibsAppx - Add-AppxPackage -Path $MsVCLibsAppx -ForceApplicationShutdown - Write-Host "Done Installing Microsoft.VCLibs.140.00.UWPDesktop" - } catch { - Write-Host "Failed to install Microsoft.VCLibs.140.00.UWPDesktop" - Write-Error $_ - } - } - - $msUiXamlPackage = Get-AppxPackage -Name "Microsoft.UI.Xaml.2.8" | Where-Object { $_.Version -ge "8.2310.30001.0" } - if (!($msUiXamlPackage)) { - # install Microsoft.UI.Xaml - try { - Write-Host "Installing Microsoft.UI.Xaml" - $MsUiXaml = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-Microsoft.UI.Xaml.2.8.6" - $MsUiXamlZip = "$($MsUiXaml).zip" - Invoke-WebRequest -Uri "https://www.nuget.org/api/v2/package/Microsoft.UI.Xaml/2.8.6" -OutFile $MsUiXamlZip - Expand-Archive $MsUiXamlZip -DestinationPath $MsUiXaml - Add-AppxPackage -Path "$($MsUiXaml)\tools\AppX\$($architecture)\Release\Microsoft.UI.Xaml.2.8.appx" -ForceApplicationShutdown - Write-Host "Done Installing Microsoft.UI.Xaml" - } catch { - Write-Host "Failed to install Microsoft.UI.Xaml" - Write-Error $_ - } - } - - $desktopAppInstallerPackage = Get-AppxPackage -Name "Microsoft.DesktopAppInstaller" - if (!($desktopAppInstallerPackage) -or ($desktopAppInstallerPackage.Version -lt "1.22.0.0")) { - # install Microsoft.DesktopAppInstaller - try { - Write-Host "Installing Microsoft.DesktopAppInstaller" - $DesktopAppInstallerAppx = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-DesktopAppInstaller.appx" - Invoke-WebRequest -Uri "https://aka.ms/getwinget" -OutFile $DesktopAppInstallerAppx - Add-AppxPackage -Path $DesktopAppInstallerAppx -ForceApplicationShutdown - Write-Host "Done Installing Microsoft.DesktopAppInstaller" - } - catch { - Write-Host "Failed to install DesktopAppInstaller appx package" - Write-Error $_ - } - } - - Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + ";C:\Program Files\PowerShell\7" - Write-Host "WinGet version: $(winget -v)" - } - - # Revert PSGallery installation policy to untrusted - Set-PSRepository -Name "PSGallery" -InstallationPolicy Untrusted - pwsh.exe -MTA -Command "Set-PSRepository -Name PSGallery -InstallationPolicy Untrusted" -} - - -function InstallPackage{ - param ( - [string]$PackageId, - [string]$PackageCommand, - [string]$PackagePath - ) - - # install package if it's not already installed - if (!(Get-Command $PackageCommand -ErrorAction SilentlyContinue)) { - # if winget is available, use it to install package - if (Get-Command winget -ErrorAction SilentlyContinue) { - Write-Host "Installing $PackageId with winget" - winget install --id $PackageId -e --source winget --silent - $installExitCode = $LASTEXITCODE - Write-Host "'winget install --id $PackageId -e --source winget --silent' exited with code: $($installExitCode)" - if ($installExitCode -eq 0) { - # add package path to path - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + ";" + $PackagePath - } - } - - # If we reached here without being able to install the package, try with Install-WinGetPackage - if (!(Get-Command $PackageCommand -ErrorAction SilentlyContinue)) { - # install winget and use that to install packages - - # winget is not available for system context, so we need to leverage WingGet cmdlets for system context. - # The WinGet cmdlets aren't supported in PowerShell 5, so we need to install Powershell7 here. - InstallPS7 - InstallWinGet - - # install package via winget client - Write-Host "Installing $PackageId with Install-WinGetPackage" - $mtaFlag = "-MTA" - $scopeFlagValue = "SystemOrUnknown" - if ($PsInstallScope -eq "CurrentUser") { - $mtaFlag = "" - $scopeFlagValue = "UserOrUnknown" - } - - $tempOutFile = [System.IO.Path]::GetTempFileName() + ".out.json" - - $installCommandBlock = { - $installPackageCommand = "Install-WinGetPackage -Scope $($scopeFlagValue) -Mode Silent -Source winget -Id $($PackageId) | ConvertTo-Json -Depth 10 | Tee-Object -FilePath '$($tempOutFile)'" - $processCreation = Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{CommandLine="C:\Program Files\PowerShell\7\pwsh.exe $($mtaFlag) -Command `"$($installPackageCommand)`""} - if (!($processCreation) -or !($processCreation.ProcessId)) { - Write-Error "Failed to install $PackageId package. Process creation failed." - exit 1 - } - - $process = Get-Process -Id $processCreation.ProcessId - $handle = $process.Handle # cache process.Handle so ExitCode isn't null when we need it below - $process.WaitForExit() - $installExitCode = $process.ExitCode - if ($installExitCode -ne 0) { - Write-Error "Failed to install $PackageId with Install-WinGetPackage, error code $($installExitCode)." - # this was the last try, so exit with the install exit code - exit $installExitCode - } - - # read the output file and write it to the console - if (Test-Path -Path $tempOutFile) { - $unitResults = Get-Content -Path $tempOutFile -Raw | Out-String - Write-Host $unitResults - Remove-Item -Path $tempOutFile -Force - # If there are any errors in the package installation, we need to exit with a non-zero code - $unitResultsObject = $unitResults | ConvertFrom-Json - - # If the initial scope didn't produce an installer, retry with an "Any" scope - if (($unitResultsObject.Status -eq "NoApplicableInstallers") -and ($scopeFlagValue -ne "Any")) { - ([ref]$scopeFlagValue).Value = "Any" - .$installCommandBlock - } - - # If there are any errors in the package installation, we need to exit with a non-zero code - if ($unitResultsObject.Status -ne "Ok") { - Write-Error "There were errors installing the package." - exit 1 - } - } - else { - Write-Host "Couldn't find output file for $PackageId installation, assuming fail." - exit 1 - } - } - .$installCommandBlock - - # add git to path - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + ";" + $PackagePath - } - } -} - -InstallPackage -PackageId "Git.Git" -PackagePath "C:\Program Files\Git\cmd" -PackageCommand "git" - -Write-Host "git version: $(git --version)" - -$repoCloned = $false - -# Disable credential prompting when running under system context to make sure the clone operation doesn't hang -$credentialInteractiveFlag = @('-c', 'credential.interactive=Never') -if ($PsInstallScope -eq "CurrentUser") { - $credentialInteractiveFlag = @() -} - -if ($Pat) { - # When a PAT is provided, we'll attempt to clone the repository during provisioning time. - # If this fails, we'll try again when the user logs in. - Write-Host "Cloning repository: $($RepositoryUrl) to directory: $($TargetRepoDirectory)" - if ($Branch) { - Write-Host "Using branch: $($Branch)" - } - - # First we'll try to clone the repository using the provided PAT. - try { - # ensure the target directory doesn't exist - if (Test-Path -PathType Container $TargetRepoDirectory) { - Remove-Item -Recurse -Force $TargetRepoDirectory - } - - if (!(Test-Path -PathType Container $TargetRepoDirectory)) { - New-Item -Path $TargetRepoDirectory -ItemType Directory - } - - # Attempt to clone - Push-Location $TargetRepoDirectory - $b64pat = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("user:$Pat")) - - # Disable credential prompting here to make sure the clone operation doesn't hang - if ($Branch) { - git $credentialInteractiveFlag -c http.extraHeader="Authorization: Basic $b64pat" clone -b $Branch $RepositoryUrl . 3>&1 2>&1 - } - else { - git $credentialInteractiveFlag -c http.extraHeader="Authorization: Basic $b64pat" clone $RepositoryUrl . 3>&1 2>&1 - } - - if ($LASTEXITCODE -ne 0) { - throw "git clone exited with code: $($LASTEXITCODE)" - } - - # Mark the directory as safe for git operations - $TargetRepoDirectorySafe = $TargetRepoDirectory -replace '\\', '/' - if ($PsInstallScope -eq "CurrentUser") { - git config --global --add safe.directory "$($TargetRepoDirectorySafe)" - } - else { - git config --system --add safe.directory "$($TargetRepoDirectorySafe)" - } - - # If the code reaches this point, we've successfully cloned the repository. - Write-Host "Successfully cloned repository: $($RepositoryUrl) to directory: $($TargetRepoDirectory)" - $repoCloned = $true - } - catch { - Write-Error $_ - Write-Host "Failed to clone repository: $($RepositoryUrl) to directory: $($TargetRepoDirectory), we'll try again assuming it's an access token." - } - finally { - Pop-Location - } - - # If the repo wasn't cloned successfully, it may be that the PAT is actually an access token, which requires - # a different approach. We'll try to clone the repository using the provided PAT as an access token. - if (!$repoCloned) { - try { - - # ===== Normalize the repository clone link - - # Sample repo clone links: - # https://organization@dev.azure.com/organization/project-name/_git/sample-repo.name - # https://dev.azure.com/organization/project-name/_git/Sample-repo.name - # https://organization.visualstudio.com/project-name/_git/sample-repo.name - # https://organization.visualstudio.com/DefaultCollection/project-name/_git/sample-repo.name - - $Pattern1 = '^https://(?[a-zA-Z0-9]+)@dev.azure.com/(?[a-zA-Z0-9]+)/(?[\.\-a-zA-Z0-9]+)/_git/(?[\.\-a-zA-Z0-9]+)/?$' - $Pattern2 = '^https://dev.azure.com/(?[a-zA-Z0-9]+)/(?[\.\-a-zA-Z0-9]+)/_git/(?[\.\-a-zA-Z0-9]+)/?$' - $Pattern3 = '^https://(?[a-zA-Z0-9]+).visualstudio.com/(?[\.\-a-zA-Z0-9]+)/_git/(?[\.\-a-zA-Z0-9]+)/?$' - $Pattern4 = '^https://(?[a-zA-Z0-9]+).visualstudio.com/[Dd]efault[Cc]ollection/(?[\.\-a-zA-Z0-9]+)/_git/(?[\.\-a-zA-Z0-9]+)/?$' - - $RepositoryUrl = $RepositoryUrl.ToLower() - if ($RepositoryUrl -match $Pattern1) { - Write-Output "Match Pattern1" - } - elseif ($RepositoryUrl -match $Pattern2) { - Write-Output "Match Pattern2" - } - elseif ($RepositoryUrl -match $Pattern3) { - Write-Output "Match Pattern3" - } - elseif ($RepositoryUrl -match $Pattern4) { - Write-Output "Match Pattern4" - } - else { - throw "RepositoryUrl doesnot match any known pattern" - } - - $NormalizedRepositoryUrl = 'https://{org}:{at}@dev.azure.com/{org}/{project}/_git/{reponame}' - $NormalizedRepositoryUrl = $NormalizedRepositoryUrl.Replace('{org}', $Matches.org) - $NormalizedRepositoryUrl = $NormalizedRepositoryUrl.Replace('{project}', $Matches.project) - $NormalizedRepositoryUrl = $NormalizedRepositoryUrl.Replace('{reponame}', $Matches.reponame) - $NormalizedRepositoryUrl = $NormalizedRepositoryUrl.Replace('{at}', $Pat) - - # ensure the target directory doesn't exist - if (Test-Path -PathType Container $TargetRepoDirectory) { - Remove-Item -Recurse -Force $TargetRepoDirectory - } - - if (!(Test-Path -PathType Container $TargetRepoDirectory)) { - New-Item -Path $TargetRepoDirectory -ItemType Directory - } - - # Attempt to clone - Push-Location $TargetRepoDirectory - if ($Branch) { - git clone -b $Branch $NormalizedRepositoryUrl . 3>&1 2>&1 - } - else { - git clone $NormalizedRepositoryUrl . 3>&1 2>&1 - } - - if ($LASTEXITCODE -ne 0) { - throw "git clone exited with code: $($LASTEXITCODE)" - } - - # Mark the directory as safe for git operations - $TargetRepoDirectorySafe = $TargetRepoDirectory -replace '\\', '/' - if ($PsInstallScope -eq "CurrentUser") { - git config --global --add safe.directory "$($TargetRepoDirectorySafe)" - } - else { - git config --system --add safe.directory "$($TargetRepoDirectorySafe)" - } - - # If the code reaches this point, we've successfully cloned the repository. - Write-Host "Successfully cloned repository: $($RepositoryUrl) to directory: $($TargetRepoDirectory)" - $repoCloned = $true - } - catch { - Write-Error $_ - Write-Host "Failed to clone repository: $($RepositoryUrl) to directory: $($TargetRepoDirectory), cloning attempt will be queued for user login" - } - finally { - Pop-Location - } - } -} - -# Check if the repository is hosted in GitHub -if (!$repoCloned -and ($RepositoryUrl -match "github.com")) { - # attempt to clone without credentials - Write-Host "Attempting to clone repository: $($RepositoryUrl) to directory: $($TargetRepoDirectory) without credentials" - if ($Branch) { - Write-Host "Using branch: $($Branch)" - } - - try { - # ensure the target directory doesn't exist - if (Test-Path -PathType Container $TargetRepoDirectory) { - Remove-Item -Recurse -Force $TargetRepoDirectory - } - - if (!(Test-Path -PathType Container $TargetRepoDirectory)) { - New-Item -Path $TargetRepoDirectory -ItemType Directory - } - - # Attempt to clone - Push-Location $TargetRepoDirectory - - # Disable credential prompting here to make sure the clone operation doesn't hang - if ($Branch) { - git $credentialInteractiveFlag clone -b $Branch $RepositoryUrl . 3>&1 2>&1 - } - else { - git $credentialInteractiveFlag clone $RepositoryUrl . 3>&1 2>&1 - } - - if ($LASTEXITCODE -ne 0) { - throw "git clone exited with code: $($LASTEXITCODE)" - } - - # Mark the directory as safe for git operations - $TargetRepoDirectorySafe = $TargetRepoDirectory -replace '\\', '/' - if ($PsInstallScope -eq "CurrentUser") { - git config --global --add safe.directory "$($TargetRepoDirectorySafe)" - } - else { - git config --system --add safe.directory "$($TargetRepoDirectorySafe)" - } - - # If the code reaches this point, we've successfully cloned the repository. - Write-Host "Successfully cloned repository: $($RepositoryUrl) to directory: $($TargetRepoDirectory)" - $repoCloned = $true - } - catch { - Write-Error $_ - Write-Host "Failed to clone repository: $($RepositoryUrl) to directory: $($TargetRepoDirectory), cloning attempt will be queued for user login" - } - finally { - Pop-Location - } -} - - -if ($repoCloned) -{ - exit 0 #Success! -} - -# If the code reaches this point, we failed to clone the repository during provisioning time or -# a PAT was not provided. We'll queue the clone attempt for user login. - -# install powershell 7 -InstallPS7 - -if (!(Test-Path -PathType Leaf "$($CustomizationScriptsDir)\$($LockFile)")) { - SetupScheduledTasks -} - -Write-Host "Writing commands to user script" - -function AppendToUserScript { - Param( - [Parameter(Position=0, Mandatory=$true)] - [string]$Content - ) - - Add-Content -Path "$($CustomizationScriptsDir)\$($RunAsUserScript)" -Value $Content -} - -# Work from C:\ -AppendToUserScript "Push-Location C:\" - -# Write intent to output stream -AppendToUserScript "Write-Host 'Cloning repository: $($RepositoryUrl) to directory: $($TargetRepoDirectory)'" -if ($Branch) { - AppendToUserScript "Write-Host 'Using branch: $($Branch)'" -} - -# make directory if it doesn't exist -AppendToUserScript "if (Test-Path -PathType Container '$($TargetRepoDirectory)') {" -AppendToUserScript " Remove-Item -Recurse -Force '$($TargetRepoDirectory)'" -AppendToUserScript "}" -AppendToUserScript "if (!(Test-Path -PathType Container '$($TargetRepoDirectory)')) {" -AppendToUserScript " New-Item -Path '$($TargetRepoDirectory)' -ItemType Directory" -AppendToUserScript "}" - -# Attempt to clone the repository -AppendToUserScript "Push-Location $($TargetRepoDirectory)" -if ($Branch) { - AppendToUserScript "git clone -b $($Branch) $($RepositoryUrl) ." -} -else { - AppendToUserScript "git clone $($RepositoryUrl) ." -} - -AppendToUserScript "Pop-Location" - -# Mark the directory as safe for git operations -$TargetRepoDirectorySafe = $TargetRepoDirectory -replace '\\', '/' -AppendToUserScript "git config --global --add safe.directory '$($TargetRepoDirectorySafe)'" - -AppendToUserScript "Pop-Location" - -Write-Host "Done writing commands to user script" - -exit 0 #Success! \ No newline at end of file diff --git a/.configuration/devcenter/tasks/git-clone/runAsUser.ps1 b/.configuration/devcenter/tasks/git-clone/runAsUser.ps1 deleted file mode 100644 index 9fda5f85..00000000 --- a/.configuration/devcenter/tasks/git-clone/runAsUser.ps1 +++ /dev/null @@ -1,121 +0,0 @@ -$CustomizationScriptsDir = "C:\DevBoxCustomizations" -$LockFile = "lockfile" -$RunAsUserScript = "runAsUser.ps1" -$CleanupScript = "cleanup.ps1" -$RunAsUserTask = "DevBoxCustomizations" -$CleanupTask = "DevBoxCustomizationsCleanup" - -# Set the progress preference to silently continue -# in order to avoid progress bars in the output -# as this makes web requests very slow -# Reference: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables -$ProgressPreference = 'SilentlyContinue' - -Start-Transcript -Path $env:TEMP\scheduled-task-customization.log -Append -IncludeInvocationHeader - -Write-Host "Microsoft Dev Box - Customizations" -Write-Host "----------------------------------" -Write-Host "Setting up scheduled tasks..." - -Remove-Item -Path "$($CustomizationScriptsDir)\$($LockFile)" - -Write-Host "Updating WinGet" -# ensure NuGet provider is installed -if (!(Get-PackageProvider | Where-Object { $_.Name -eq "NuGet" -and $_.Version -gt "2.8.5.201" })) { - Write-Host "Installing NuGet provider" - Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser - Write-Host "Done Installing NuGet provider" -} -else { - Write-Host "NuGet provider is already installed" -} - -# Set PSGallery installation policy to trusted -Set-PSRepository -Name "PSGallery" -InstallationPolicy Trusted - -# check if the Microsoft.Winget.Client module is installed -if (!(Get-Module -ListAvailable -Name Microsoft.Winget.Client)) { - Write-Host "Installing Microsoft.Winget.Client" - Install-Module Microsoft.WinGet.Client -Scope CurrentUser - Write-Host "Done Installing Microsoft.Winget.Client" -} -else { - Write-Host "Microsoft.Winget.Client is already installed" -} - -# check if the Microsoft.WinGet.Configuration module is installed -if (!(Get-Module -ListAvailable -Name Microsoft.WinGet.Configuration)) { - Write-Host "Installing Microsoft.WinGet.Configuration" - pwsh.exe -MTA -Command "Install-Module Microsoft.WinGet.Configuration -AllowPrerelease -Scope CurrentUser" - Write-Host "Done Installing Microsoft.WinGet.Configuration" -} -else { - Write-Host "Microsoft.WinGet.Configuration is already installed" -} - -$architecture = "x64" -if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") { - $architecture = "arm64" -} - -$msVCLibsPackage = Get-AppxPackage -Name "Microsoft.VCLibs.140.00.UWPDesktop" | Where-Object { $_.Version -ge "14.0.33728.0" } -if (!($msVCLibsPackage)) { -# Install Microsoft.VCLibs.140.00.UWPDesktop - try { - Write-Host "Installing Microsoft.VCLibs.140.00.UWPDesktop" - $MsVCLibs = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-Microsoft.VCLibs.140.00.UWPDesktop" - $MsVCLibsAppx = "$($MsVCLibs).appx" - - Invoke-WebRequest -Uri "https://aka.ms/Microsoft.VCLibs.$($architecture).14.00.Desktop.appx" -OutFile $MsVCLibsAppx - Add-AppxPackage -Path $MsVCLibsAppx -ForceApplicationShutdown - Write-Host "Done Installing Microsoft.VCLibs.140.00.UWPDesktop" - } catch { - Write-Host "Failed to install Microsoft.VCLibs.140.00.UWPDesktop" - Write-Error $_ - } -} - -$msUiXamlPackage = Get-AppxPackage -Name "Microsoft.UI.Xaml.2.8" | Where-Object { $_.Version -ge "8.2310.30001.0" } -if (!($msUiXamlPackage)) { - # install Microsoft.UI.Xaml - try{ - Write-Host "Installing Microsoft.UI.Xaml" - $MsUiXaml = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-Microsoft.UI.Xaml.2.8.6" - $MsUiXamlZip = "$($MsUiXaml).zip" - Invoke-WebRequest -Uri "https://www.nuget.org/api/v2/package/Microsoft.UI.Xaml/2.8.6" -OutFile $MsUiXamlZip - Expand-Archive $MsUiXamlZip -DestinationPath $MsUiXaml - Add-AppxPackage -Path "$($MsUiXaml)\tools\AppX\$($architecture)\Release\Microsoft.UI.Xaml.2.8.appx" -ForceApplicationShutdown - Write-Host "Done Installing Microsoft.UI.Xaml" - } catch { - Write-Host "Failed to install Microsoft.UI.Xaml" - Write-Error $_ - } -} - -$desktopAppInstallerPackage = Get-AppxPackage -Name "Microsoft.DesktopAppInstaller" -if (!($desktopAppInstallerPackage) -or ($desktopAppInstallerPackage.Version -lt "1.22.0.0")) { - # install Microsoft.DesktopAppInstaller - try { - Write-Host "Installing Microsoft.DesktopAppInstaller" - $DesktopAppInstallerAppx = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-DesktopAppInstaller.appx" - Invoke-WebRequest -Uri "https://aka.ms/getwinget" -OutFile $DesktopAppInstallerAppx - - # install the DesktopAppInstaller appx package - Add-AppxPackage -Path $DesktopAppInstallerAppx -ForceApplicationShutdown - - Write-Host "Done Installing Microsoft.DesktopAppInstaller" - } - catch { - Write-Host "Failed to install DesktopAppInstaller appx package" - Write-Error $_ - } -} - -Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe -Write-Host "WinGet version: $(winget -v)" - -$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") -Write-Host "Done Updating WinGet" - -# Revert PSGallery installation policy to untrusted -Set-PSRepository -Name "PSGallery" -InstallationPolicy Untrusted \ No newline at end of file diff --git a/.configuration/devcenter/tasks/git-clone/task.yaml b/.configuration/devcenter/tasks/git-clone/task.yaml deleted file mode 100644 index efa04e05..00000000 --- a/.configuration/devcenter/tasks/git-clone/task.yaml +++ /dev/null @@ -1,52 +0,0 @@ -# This is a git repository cloning task for Dev Box - -$schema: 1.0 -name: git-clone -description: Clones a git repository. -author: Microsoft Corporation -command: .\main.ps1 -RepositoryUrl {{ repositoryUrl }} -Directory {{ directory }} -Branch {{ branch }} -Pat {{ pat }} -parameters: - repositoryUrl: - default: '' - type: string - required: true - description: The URL of the repository to clone. - directory: - default: C:\Workspaces - type: string - required: false - description: | - The directory under which the repository will be cloned. This is the - equivalent of the working directory under which the git command will - execute. - branch: - default: '' - type: string - required: false - description: The branch to clone. - pat: - default: '' - type: string - required: false - description: The PAT (personal access token) to use when cloning the repository. -documentation: - notes: | - This task allows you to clone a repository. When the "pat" parameter is - not specified, the task will queue the cloning operation to happen on the - first user login. Otherwise the task will clone the repository during the - dev box provisioning stage. - We encourage you to pass a reference uri to a KeyVault secret as the value - for the "pat" parameter instead of including the Raw secret value in yamls. - Note that Powershell7, Git will be installed during this task. - examples: - - name: git-clone - description: Clone this repository into C:\Workspaces - parameters: - repositoryUrl: https://github.com/microsoft/calculator - directory: C:\Workspaces - - name: git-clone - description: Clone this repository's feature/winui2.6 branch into C:\Workspaces - parameters: - repositoryUrl: https://github.com/microsoft/calculator - directory: C:\Workspaces - branch: feature/winui2.6 \ No newline at end of file diff --git a/.configuration/devcenter/tasks/install-vs-extension/Install-VSIXExtension.ps1 b/.configuration/devcenter/tasks/install-vs-extension/Install-VSIXExtension.ps1 deleted file mode 100644 index 240e8ca1..00000000 --- a/.configuration/devcenter/tasks/install-vs-extension/Install-VSIXExtension.ps1 +++ /dev/null @@ -1,195 +0,0 @@ -<# -.SYNOPSIS - Install an extension defined by the given item name to the latest Visual Studio instance -.PARAMETER MarketplaceItemName - Markplace Item Name (as used in the URI of a given Visual Studio Extension Maketplace entry) -#> -param ( - [Parameter(Mandatory)] - [string]$MarketplaceItemName - ) - -<# -.SYNOPSIS - Download an extension defined by the given item name -.PARAMETER MarketplaceItemName - Markplace Item Name (as used in the URI of a given Visual Studio Extension Maketplace entry) -#> - -function Get-VSIXFromMarketplace -{ - param ( - [Parameter(Mandatory)] - [string]$MarketplaceItemName - ) - - # Declare exit code. Default to failure and set to 0 when operation succeeds. - $exitCode = 1 - - # Turn off progress to fix speed bug in Invoke-WebRequest - $ProgressPreference = 'SilentlyContinue' - - $vswhereResult = Invoke-VsWhere "-prerelease -latest -format json" | ConvertFrom-Json - - $instanceVersion = $vswhereResult.InstallationVersion - - $isolationInfo = ConvertFrom-StringData((Get-Content "$($vswhereResult.ProductPath -replace ".exe",".isolation.ini")" | Select-Object -Skip 2) -replace "\\","\\\\" -join "`n") - $arch = If ($isolationInfo.ProductArch -eq "x64") { "amd64" } else { $isolationInfo.ProductArch } - - $marketplaceQueryBody = -@" -{"flags":"673","filters":[{"criteria":[{"filterType":"7","value":"$MarketplaceItemName"},{"filterType":"15","value":"$instanceVersion"},{"filterType":"22","value":"$arch"},{"filterType":"8","value":"Microsoft.VisualStudio.Enterprise"},{"filterType":"8","value":"Microsoft.VisualStudio.Ultimate"},{"filterType":"8","value":"Microsoft.VisualStudio.Pro"},{"filterType":"8","value":"Microsoft.VisualStudio.Community"},{"filterType":"8","value":"Microsoft.VisualStudio.IntegratedShell"}],"sortBy":"4","sortOrder":"2","pageSize":"2","pageNumber":"1"}]} -"@ - - $requestParams = @{ - Uri = "https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery" - Method = "Post" - Headers = @{"Accept" = 'application/json;api-version=3.2-preview.1'} - ContentType = "application/json" - Body = $marketplaceQueryBody - UseBasicParsing = $true - } - - $jsonResponse = (Invoke-WebRequest @requestParams).Content | ConvertFrom-Json - $extensions = $jsonResponse.results.extensions - - if(-not $extensions -or ($extensions -is [array] -and $extensions.length -le 0)) { - Write-Warning "No extension was found for item: $MarketplaceItemName. Specify a valid extension." - exit $exitcode - } - - if($extensions -is [array] -and $extensions.length -gt 1) { - Write-Warning "Multiple extensions ($($extensions.length)) found for the given item name $MarketplaceItemName" - exit $exitcode - } - - $cdnUrl = "$($extensions.versions[0].fallbackAssetUri)/Microsoft.VisualStudio.IDE.Payload?redirect=true&install=true" - $downloadFilePath = Join-Path ([IO.Path]::GetTempPath()) ([IO.Path]::ChangeExtension("VSIX$([IO.Path]::GetRandomFileName())", ".vsix")) - $extensionName = $extensions.displayName - Write-Host "Downloading $extensionName" - - Invoke-WebRequest $cdnUrl -UseBasicParsing -OutFile $downloadFilePath - - # Turn progress back on - $ProgressPreference = 'Continue' - - return $downloadFilePath -} - -<# -.SYNOPSIS - Invokes Visual Studio Locator, if it exists, with the provided arguments. -.DESCRIPTION - Invokes Visual Studio Locator (vswhere.exe) with the provided arguments. - If this script is run without the locator present, it will fail. -.PARAMETER Arguments - Arguments to pass onwards to Visual Studio Locator. -.LINK - https://learn.microsoft.com/en-us/visualstudio/install/tools-for-managing-visual-studio-instances#using-vswhereexe -#> -function Invoke-VsWhere -{ - param - ( - [Parameter(Mandatory)] - [string]$Arguments - ) - - Assert-VsWherePresent - - return Invoke-Expression -Command "&'$(Get-VsWherePath)' $Arguments" -} - -<# -.SYNOPSIS - Returns the default path of Visual Studio Locator (vswhere.exe). -#> -function Get-VsWherePath -{ - return Join-Path -Path "${env:ProgramFiles(x86)}" -ChildPath "Microsoft Visual Studio\Installer\vswhere.exe" -} - -<# -.SYNOPSIS - Throws an exception if Visual Studio Locator (vswhere.exe) is not present in the default location. -#> -function Assert-VsWherePresent -{ - if(-not (Test-Path (Get-VsWherePath))) - { - throw "Visual Studio Locator not found." - exit $exitcode - } -} - -<# -.SYNOPSIS - Invokes VSIX Installer, if it exists, with the provided arguments. -.DESCRIPTION - Invokes VSIX Installer with the provided arguments. - If this script is run without VSIX Installer present, it will fail. -.PARAMETER Arguments - Arguments to pass onwards to VSIX Installer. -#> -function Invoke-VsixInstaller -{ - param - ( - [Parameter(Mandatory)] - [string]$Arguments - ) - - Assert-VsixInstallerPresent - - return Invoke-Expression -Command "&'$(Get-VsixInstallerPath)' $Arguments" -} - -<# -.SYNOPSIS - Returns the default path of VSIX Installer. -#> -function Get-VsixInstallerPath -{ - return Join-Path -Path "${env:ProgramFiles(x86)}" -ChildPath "Microsoft Visual Studio\Installer\resources\app\ServiceHub\Services\Microsoft.VisualStudio.Setup.Service\VSIXInstaller.exe" -} - -<# -.SYNOPSIS - Throws an exception if VSIX Installer is not present in the default location. -#> -function Assert-VsixInstallerPresent -{ - if(-not (Test-Path (Get-VsixInstallerPath))) - { - throw "VSIX Installer not found." - exit $exitcode - } -} - -# ---- Main Script Start ---- - -$pathToVSIX = Get-VSIXFromMarketplace $MarketplaceItemName - -if(-not $pathToVsix -or -not (Test-Path $pathToVSIX -PathType Leaf)) { - Write-Warning "Issue with VSIX path for item $MarketplaceItemName. No extension downloaded; skipping install." - exit $exitcode -} - -Write-Host "Invoking VSIX Installer for downloaded VSIX at $pathToVsix..." - -try { - Invoke-VsixInstaller "/a /enableUpdate /q /f /sp $pathToVSIX" | Out-Null - Wait-Process (Get-Process VsixInstaller).id -Timeout 600 -} -catch { - Write-Warning "VSIX Installer failed with error: $_" - exit $exitcode -} - -Write-Host "VSIX Installer Completed." -Write-Host "$MarketplaceItemName Successfully installed." - -$exitcode = 0 -exit $exitCode - -# ---- Main Script End ---- diff --git a/.configuration/devcenter/tasks/install-vs-extension/task.yaml b/.configuration/devcenter/tasks/install-vs-extension/task.yaml deleted file mode 100644 index 3b5c67be..00000000 --- a/.configuration/devcenter/tasks/install-vs-extension/task.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# This is a Visual Studio extension installation task for Dev Box. - -$schema: 1.0 -name: install-vs-extension -description: Installs a Visual Studio extension -author: Microsoft Corporation -command: ".\\Install-VSIXExtension.ps1 -MarketplaceItemName {{marketplaceItemName}}" -parameters: - marketplaceItemName: - type: string - required: true - description: | - The name of the Visual Studio extension to install. - Visit https://marketplace.visualstudio.com to learn - more about Visual Studio extensions. -documentation: - notes: This task is used to install a Visual Studio extension. - examples: - - name: install-vs-extension - description: Install GitHub Copilot - parameters: - marketplaceItemName: GitHub.copilotvs diff --git a/.configuration/devcenter/tasks/powershell/Run-Command.ps1 b/.configuration/devcenter/tasks/powershell/Run-Command.ps1 deleted file mode 100644 index c29d6200..00000000 --- a/.configuration/devcenter/tasks/powershell/Run-Command.ps1 +++ /dev/null @@ -1,35 +0,0 @@ -param( - [Parameter()] - [string]$Command, - [Parameter()] - [string]$WorkingDirectory - ) - -# Check if workingDirectory is set and not empty and if so, change to it. -if ($WorkingDirectory -and $WorkingDirectory -ne "") { - # Check if the working directory exists. - if (-not (Test-Path $WorkingDirectory)) { - # Create the working directory if it does not exist. - Write-Output "Creating working directory $WorkingDirectory" - New-Item -ItemType Directory -Force -Path $WorkingDirectory - } - - Write-Output "Changing to working directory $WorkingDirectory" - Set-Location $WorkingDirectory -} - -# Note we're calling powershell.exe directly, instead -# of running Invoke-Expression, as suggested by -# https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/avoid-using-invoke-expression?view=powershell-7.3 -# Note that this will run powershell.exe -# even if the system has pwsh.exe. -Write-Output "Running command $Command" -powershell.exe -Command $Command -$CommandExitCode = $LASTEXITCODE -Write-Output "Command exited with code $CommandExitCode" - -# Task powershell scripts should always end with an -# exit code reported up to the runner agent. -# This is how the runner agent knows whether the -# command succeeded or failed. -exit $CommandExitCode diff --git a/.configuration/devcenter/tasks/powershell/task.yaml b/.configuration/devcenter/tasks/powershell/task.yaml deleted file mode 100644 index 90ae9ba3..00000000 --- a/.configuration/devcenter/tasks/powershell/task.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# This is a simple powershell command execution task for Dev Box. - -$schema: 1.0 -name: powershell -description: Execute a powershell command. -author: Microsoft Corporation -command: ".\\Run-Command.ps1 -Command {{command}} -WorkingDirectory {{workingDirectory}}" -parameters: - command: - type: string - default: "" - required: true - description: The command to execute. - workingDirectory: - type: string - default: "" - required: false - description: The working directory to execute the command in. -documentation: - notes: This task is used to execute a powershell command. - examples: - - name: powershell - description: print hello world - parameters: - command: "Write-Host 'hello, world!'" - - name: powershell - description: run script - parameters: - command: "./script.ps1" - workingDirectory: C:\\provisioning\\temp diff --git a/.configuration/devcenter/tasks/winget/cleanup.ps1 b/.configuration/devcenter/tasks/winget/cleanup.ps1 deleted file mode 100644 index 1e156f99..00000000 --- a/.configuration/devcenter/tasks/winget/cleanup.ps1 +++ /dev/null @@ -1,13 +0,0 @@ -$CustomizationScriptsDir = "C:\DevBoxCustomizations" -$LockFile = "lockfile" -$SetVariablesScript = "setVariables.ps1" -$RunAsUserScript = "runAsUser.ps1" -$CleanupScript = "cleanup.ps1" -$RunAsUserTask = "DevBoxCustomizations" -$CleanupTask = "DevBoxCustomizationsCleanup" - -if (!(Test-Path "$($CustomizationScriptsDir)\$($LockFile)")) { - Unregister-ScheduledTask -TaskName $RunAsUserTask -Confirm:$false - Unregister-ScheduledTask -TaskName $CleanupTask -Confirm:$false - Remove-Item $CustomizationScriptsDir -Force -Recurse -} diff --git a/.configuration/devcenter/tasks/winget/main.ps1 b/.configuration/devcenter/tasks/winget/main.ps1 deleted file mode 100644 index cddcf65b..00000000 --- a/.configuration/devcenter/tasks/winget/main.ps1 +++ /dev/null @@ -1,460 +0,0 @@ -param ( - [Parameter()] - [string]$ConfigurationFile, - [Parameter()] - [string]$DownloadUrl, - [Parameter()] - [string]$InlineConfigurationBase64, - [Parameter()] - [string]$Package, - [Parameter()] - [string]$Version = '', - [Parameter()] - [string]$RunAsUser -) - -$CustomizationScriptsDir = "C:\DevBoxCustomizations" -$LockFile = "lockfile" -$RunAsUserScript = "runAsUser.ps1" -$CleanupScript = "cleanup.ps1" -$RunAsUserTask = "DevBoxCustomizations" -$CleanupTask = "DevBoxCustomizationsCleanup" -$PsInstallScope = "CurrentUser" -if ($(whoami.exe) -eq "nt authority\system") { - $PsInstallScope = "AllUsers" -} - -# Set the progress preference to silently continue -# in order to avoid progress bars in the output -# as this makes web requests very slow -# Reference: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables -$ProgressPreference = 'SilentlyContinue' - -function SetupScheduledTasks { - Write-Host "Setting up scheduled tasks" - if (!(Test-Path -PathType Container $CustomizationScriptsDir)) { - New-Item -Path $CustomizationScriptsDir -ItemType Directory - } - - if (!(Test-Path -PathType Leaf "$($CustomizationScriptsDir)\$($LockFile)")) { - New-Item -Path "$($CustomizationScriptsDir)\$($LockFile)" -ItemType File - } - - if (!(Test-Path -PathType Leaf "$($CustomizationScriptsDir)\$($RunAsUserScript)")) { - Copy-Item "./$($RunAsUserScript)" -Destination $CustomizationScriptsDir - } - - if (!(Test-Path -PathType Leaf "$($CustomizationScriptsDir)\$($CleanupScript)")) { - Copy-Item "./$($CleanupScript)" -Destination $CustomizationScriptsDir - } - - # Reference: https://learn.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-objects - $ShedService = New-Object -comobject "Schedule.Service" - $ShedService.Connect() - - # Schedule the cleanup script to run every minute as SYSTEM - $Task = $ShedService.NewTask(0) - $Task.RegistrationInfo.Description = "Dev Box Customizations Cleanup" - $Task.Settings.Enabled = $true - $Task.Settings.AllowDemandStart = $false - - $Trigger = $Task.Triggers.Create(9) - $Trigger.Enabled = $true - $Trigger.Repetition.Interval="PT1M" - - $Action = $Task.Actions.Create(0) - $Action.Path = "PowerShell.exe" - $Action.Arguments = "Set-ExecutionPolicy Bypass -Scope Process -Force; $($CustomizationScriptsDir)\$($CleanupScript)" - - $TaskFolder = $ShedService.GetFolder("\") - $TaskFolder.RegisterTaskDefinition("$($CleanupTask)", $Task , 6, "NT AUTHORITY\SYSTEM", $null, 5) - - # Schedule the script to be run in the user context on login - $Task = $ShedService.NewTask(0) - $Task.RegistrationInfo.Description = "Dev Box Customizations" - $Task.Settings.Enabled = $true - $Task.Settings.AllowDemandStart = $false - $Task.Principal.RunLevel = 1 - - $Trigger = $Task.Triggers.Create(9) - $Trigger.Enabled = $true - - $Action = $Task.Actions.Create(0) - $Action.Path = "C:\Program Files\PowerShell\7\pwsh.exe" - $Action.Arguments = "-MTA -Command $($CustomizationScriptsDir)\$($RunAsUserScript)" - - $TaskFolder = $ShedService.GetFolder("\") - $TaskFolder.RegisterTaskDefinition("$($RunAsUserTask)", $Task , 6, "Users", $null, 4) - Write-Host "Done setting up scheduled tasks" -} - -function WithRetry { - Param( - [Parameter(Position=0, Mandatory=$true)] - [scriptblock]$ScriptBlock, - - [Parameter(Position=1, Mandatory=$false)] - [int]$Maximum = 5, - - [Parameter(Position=2, Mandatory=$false)] - [int]$Delay = 100 - ) - - $iterationCount = 0 - $lastException = $null - do { - $iterationCount++ - try { - Invoke-Command -Command $ScriptBlock - return - } catch { - $lastException = $_ - Write-Error $_ - - # Sleep for a random amount of time with exponential backoff - $randomDouble = Get-Random -Minimum 0.0 -Maximum 1.0 - $k = $randomDouble * ([Math]::Pow(2.0, $iterationCount) - 1.0) - Start-Sleep -Milliseconds ($k * $Delay) - } - } while ($iterationCount -lt $Maximum) - - throw $lastException -} - -function InstallPS7 { - if (!(Get-Command pwsh -ErrorAction SilentlyContinue)) { - Write-Host "Installing PowerShell 7" - $code = Invoke-RestMethod -Uri https://aka.ms/install-powershell.ps1 - $null = New-Item -Path function:Install-PowerShell -Value $code - WithRetry -ScriptBlock { - if ("$($PsInstallScope)" -eq "CurrentUser") { - Install-PowerShell -UseMSI - } - else { - # The -Quiet flag requires admin permissions - Install-PowerShell -UseMSI -Quiet - } - } -Maximum 5 -Delay 100 - # Need to update the path post install - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + ";C:\Program Files\PowerShell\7" - Write-Host "Done Installing PowerShell 7" - } - else { - Write-Host "PowerShell 7 is already installed" - } -} - -function InstallWinGet { - Write-Host "Installing powershell modules in scope: $PsInstallScope" - - # ensure NuGet provider is installed - if (!(Get-PackageProvider | Where-Object { $_.Name -eq "NuGet" -and $_.Version -gt "2.8.5.201" })) { - Write-Host "Installing NuGet provider" - Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope $PsInstallScope - Write-Host "Done Installing NuGet provider" - } - else { - Write-Host "NuGet provider is already installed" - } - - # Set PSGallery installation policy to trusted - Set-PSRepository -Name "PSGallery" -InstallationPolicy Trusted - pwsh.exe -MTA -Command "Set-PSRepository -Name PSGallery -InstallationPolicy Trusted" - - # check if the Microsoft.Winget.Client module is installed - $wingetClientPackage = pwsh.exe -Command "Get-Module -ListAvailable -Name Microsoft.WinGet.Client | Where-Object { `$_.Version -ge '1.9.2411' }" - if (!($wingetClientPackage)) { - Write-Host "Installing Microsoft.Winget.Client" - pwsh.exe -MTA -Command "Install-Module Microsoft.WinGet.Client -Scope $PsInstallScope" - Write-Host "Done Installing Microsoft.Winget.Client" - } - else { - Write-Host "Microsoft.Winget.Client is already installed" - } - - # check if the Microsoft.WinGet.Configuration module is installed - $wingetConfigurationPackage = pwsh.exe -Command "Get-Module -ListAvailable -Name Microsoft.WinGet.Configuration | Where-Object { `$_.Version -ge '1.8.1911' }" - if (!($wingetConfigurationPackage)) { - Write-Host "Installing Microsoft.WinGet.Configuration" - pwsh.exe -MTA -Command "Install-Module Microsoft.WinGet.Configuration -Scope $PsInstallScope" - Write-Host "Done Installing Microsoft.WinGet.Configuration" - } - else { - Write-Host "Microsoft.WinGet.Configuration is already installed" - } - - Write-Host "Updating WinGet" - try { - Write-Host "Attempting to repair WinGet Package Manager" - pwsh.exe -MTA -Command "Repair-WinGetPackageManager -Latest -Force -Verbose" - Write-Host "Done Reparing WinGet Package Manager" - } - catch { - Write-Host "Failed to repair WinGet Package Manager" - Write-Error $_ - } - - if ($PsInstallScope -eq "CurrentUser") { - - $architecture = "x64" - if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") { - $architecture = "arm64" - } - - $msVCLibsPackage = Get-AppxPackage -Name "Microsoft.VCLibs.140.00.UWPDesktop" | Where-Object { $_.Version -ge "14.0.30035.0" } - if (!($msVCLibsPackage)) { - # Install Microsoft.VCLibs.140.00.UWPDesktop - try { - Write-Host "Installing Microsoft.VCLibs.140.00.UWPDesktop" - $MsVCLibs = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-Microsoft.VCLibs.140.00.UWPDesktop" - $MsVCLibsAppx = "$($MsVCLibs).appx" - - Invoke-WebRequest -Uri "https://aka.ms/Microsoft.VCLibs.$($architecture).14.00.Desktop.appx" -OutFile $MsVCLibsAppx - Add-AppxPackage -Path $MsVCLibsAppx -ForceApplicationShutdown - Write-Host "Done Installing Microsoft.VCLibs.140.00.UWPDesktop" - } catch { - Write-Host "Failed to install Microsoft.VCLibs.140.00.UWPDesktop" - Write-Error $_ - } - } - - $msUiXamlPackage = Get-AppxPackage -Name "Microsoft.UI.Xaml.2.8" | Where-Object { $_.Version -ge "8.2310.30001.0" } - if (!($msUiXamlPackage)) { - # instal Microsoft.UI.Xaml - try { - Write-Host "Installing Microsoft.UI.Xaml" - $MsUiXaml = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-Microsoft.UI.Xaml.2.8.6" - $MsUiXamlZip = "$($MsUiXaml).zip" - Invoke-WebRequest -Uri "https://www.nuget.org/api/v2/package/Microsoft.UI.Xaml/2.8.6" -OutFile $MsUiXamlZip - Expand-Archive $MsUiXamlZip -DestinationPath $MsUiXaml - Add-AppxPackage -Path "$($MsUiXaml)\tools\AppX\$($architecture)\Release\Microsoft.UI.Xaml.2.8.appx" -ForceApplicationShutdown - Write-Host "Done Installing Microsoft.UI.Xaml" - } catch { - Write-Host "Failed to install Microsoft.UI.Xaml" - Write-Error $_ - } - } - - $desktopAppInstallerPackage = Get-AppxPackage -Name "Microsoft.DesktopAppInstaller" - if (!($desktopAppInstallerPackage) -or ($desktopAppInstallerPackage.Version -lt "1.22.0.0")) { - # install Microsoft.DesktopAppInstaller - try { - Write-Host "Installing Microsoft.DesktopAppInstaller" - $DesktopAppInstallerAppx = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-DesktopAppInstaller.appx" - Invoke-WebRequest -Uri "https://aka.ms/getwinget" -OutFile $DesktopAppInstallerAppx - Add-AppxPackage -Path $DesktopAppInstallerAppx -ForceApplicationShutdown - Write-Host "Done Installing Microsoft.DesktopAppInstaller" - } - catch { - Write-Host "Failed to install DesktopAppInstaller appx package" - Write-Error $_ - } - } - - Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + ";C:\Program Files\PowerShell\7" - Write-Host "WinGet version: $(winget -v)" - } - - # Revert PSGallery installation policy to untrusted - Set-PSRepository -Name "PSGallery" -InstallationPolicy Untrusted - pwsh.exe -MTA -Command "Set-PSRepository -Name PSGallery -InstallationPolicy Untrusted" -} - -InstallPS7 -InstallWinGet - -function AppendToUserScript { - Param( - [Parameter(Position=0, Mandatory=$true)] - [string]$Content - ) - - Add-Content -Path "$($CustomizationScriptsDir)\$($RunAsUserScript)" -Value $Content -} - -function EnsureConfigurationFileIsSet ($ConfigurationFile) { - # if $ConfigurationFile is not specified, we need to write the configuration to a temporary file - if (-not $ConfigurationFile) { - if ($RunAsUser -eq "true") { - # when running as user, we need to write the configuration to a file in the customization scripts directory - $ConfigurationFile = "$($CustomizationScriptsDir)\$([System.IO.Path]::GetRandomFileName()).yaml" - } - else { - # when running in the provisioning context, we need to write the configuration to a temporary file - # when this is run as system, it will end up somewhere under C:\Windows\system32\config\systemprofile\AppData\Local\Temp\ - # when running as a user, it will end up somewhere under C:\Users\\AppData\Local\Temp\ - $ConfigurationFile = [System.IO.Path]::GetTempFileName() + ".yaml" - } - } - - # Ensure the directory exists - $ConfigurationFileDir = Split-Path -Path $ConfigurationFile - if(-Not (Test-Path -Path $ConfigurationFileDir)) { - $null = New-Item -ItemType Directory -Path $ConfigurationFileDir - } - - return $ConfigurationFile -} - -# If an inline base64 configuration is specified, we need to write the decoded version to the file -if ($InlineConfigurationBase64) { - Write-Host "Decoding base64 inline configuration and writing to file" - - $ConfigurationFile = EnsureConfigurationFileIsSet($ConfigurationFile) - $InlineConfiguration = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($InlineConfigurationBase64)) - $InlineConfiguration | Out-File -FilePath $ConfigurationFile -Encoding utf8 - - Write-Host "Wrote configuration to file: $($ConfigurationFile)" -} -# If a download URL is specified, we need to download the contents and write them to the file -elseif ($DownloadUrl) { - Write-Host "Downloading configuration file from: $($DownloadUrl)" - - $ConfigurationFile = EnsureConfigurationFileIsSet($ConfigurationFile) - Invoke-WebRequest -Uri $DownloadUrl -OutFile $ConfigurationFile - - Write-Host "Downloaded configuration to: $($ConfigurationFile)" -} - -$versionFlag = "" -# We're running as user via scheduled task: -if ($RunAsUser -eq "true") { - Write-Host "Running as user via scheduled task" - - if (!(Test-Path -PathType Leaf "$($CustomizationScriptsDir)\$($LockFile)")) { - SetupScheduledTasks - } - - Write-Host "Writing commands to user script" - - # We're running in package mode: - if ($Package) { - # If there's a version passed, add the version flag for CLI - if ($Version -ne '') { - Write-Host "Specifying version: $($Version)" - $versionFlag = "--version `"$($Version)`"" - } - Write-Host "Appending package install: $($Package)" - AppendToUserScript "winget install --id `"$($Package)`" $($versionFlag) --accept-source-agreements --accept-package-agreements --silent" - AppendToUserScript "Write-Host `"winget exit code: `$LASTEXITCODE`"" - } - # We're running in configuration file mode: - elseif ($ConfigurationFile) { - Write-Host "Appending installation of configuration file: $($ConfigurationFile)" - AppendToUserScript "winget configure --file `"$($ConfigurationFile)`" --accept-configuration-agreements" - AppendToUserScript "Write-Host `"winget exit code: `$LASTEXITCODE`"" - } - else { - Write-Error "No package or configuration file specified" - exit 1 - } -} -# We're running in the provisioning context: -else { - Write-Host "Running in the provisioning context" - $tempOutFile = [System.IO.Path]::GetTempFileName() + ".out.json" - - $mtaFlag = "-MTA" - $scopeFlagValue = "SystemOrUnknown" - if ($PsInstallScope -eq "CurrentUser") { - $mtaFlag = "" - $scopeFlagValue = "UserOrUnknown" - } - - # We're running in package mode: - if ($Package) { - Write-Host "Running package install: $($Package)" - # If there's a version passed, add the version flag for PS - if ($Version -ne '') { - Write-Host "Specifying version: $($Version)" - $versionFlag = "-Version '$($Version)'" - } - - $installCommandBlock = { - $installPackageCommand = "Install-WinGetPackage -Scope $($scopeFlagValue) -Mode Silent -Source winget -Id '$($Package)' $($versionFlag) | ConvertTo-Json -Depth 10 | Tee-Object -FilePath '$($tempOutFile)'" - $processCreation = Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{CommandLine="C:\Program Files\PowerShell\7\pwsh.exe $($mtaFlag) -Command `"$($installPackageCommand)`""} - if (!($processCreation) -or !($processCreation.ProcessId)) { - Write-Error "Failed to install package. Process creation failed." - exit 1 - } - - $process = Get-Process -Id $processCreation.ProcessId - $handle = $process.Handle # cache process.Handle so ExitCode isn't null when we need it below - $process.WaitForExit() - $installExitCode = $process.ExitCode - if ($installExitCode -ne 0) { - Write-Error "Failed to install package. Exit code: $($installExitCode)." - exit 1 - } - - # read the output file and write it to the console - if (Test-Path -Path $tempOutFile) { - $unitResults = Get-Content -Path $tempOutFile -Raw | Out-String - Write-Host $unitResults - Remove-Item -Path $tempOutFile -Force - $unitResultsObject = $unitResults | ConvertFrom-Json - - # If the initial scope didn't produce an installer, retry with an "Any" scope - if (($unitResultsObject.Status -eq "NoApplicableInstallers") -and ($scopeFlagValue -ne "Any")) { - ([ref]$scopeFlagValue).Value = "Any" - .$installCommandBlock - } - - # If there are any errors in the package installation, we need to exit with a non-zero code - if ($unitResultsObject.Status -ne "Ok") { - Write-Error "There were errors installing the package." - exit 1 - } - } - else { - Write-Host "Couldn't find output file for package installation, assuming fail." - exit 1 - } - } - .$installCommandBlock - } - # We're running in configuration file mode: - elseif ($ConfigurationFile) { - Write-Host "Running installation of configuration file: $($ConfigurationFile)" - $applyConfigCommand = "Get-WinGetConfiguration -File '$($ConfigurationFile)' | Invoke-WinGetConfiguration -AcceptConfigurationAgreements | Select-Object -ExpandProperty UnitResults | ConvertTo-Json -Depth 10 | Tee-Object -FilePath '$($tempOutFile)'" - $processCreation = Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{CommandLine="C:\Program Files\PowerShell\7\pwsh.exe -Command `"$($applyConfigCommand)`""} - if (!($processCreation) -or !($processCreation.ProcessId)) { - Write-Error "Failed to run configuration file installation. Process creation failed." - exit 1 - } - - $process = Get-Process -Id $processCreation.ProcessId - $handle = $process.Handle # cache process.Handle so ExitCode isn't null when we need it below - $process.WaitForExit() - $installExitCode = $process.ExitCode - if ($installExitCode -ne 0) { - Write-Error "Failed to run configuration file installation. Exit code: $($installExitCode)." - exit 1 - } - - # read the output file and write it to the console - if (Test-Path -Path $tempOutFile) { - $unitResults = Get-Content -Path $tempOutFile -Raw | Out-String - Write-Host $unitResults - Remove-Item -Path $tempOutFile -Force - # If there are any errors in the unit results, we need to exit with a non-zero code - $unitResultsObject = $unitResults | ConvertFrom-Json - $errors = $unitResultsObject | Where-Object { $_.ResultCode -ne "0" } - if ($errors) { - Write-Error "There were errors applying the configuration." - exit 1 - } - } - else { - Write-Host "Couldn't find output file for configuration application, assuming fail." - exit 1 - } - } - else { - Write-Error "No package or configuration file specified" - exit 1 - } -} - -exit 0 \ No newline at end of file diff --git a/.configuration/devcenter/tasks/winget/runAsUser.ps1 b/.configuration/devcenter/tasks/winget/runAsUser.ps1 deleted file mode 100644 index dc6c3599..00000000 --- a/.configuration/devcenter/tasks/winget/runAsUser.ps1 +++ /dev/null @@ -1,124 +0,0 @@ -$CustomizationScriptsDir = "C:\DevBoxCustomizations" -$LockFile = "lockfile" -$SetVariablesScript = "setVariables.ps1" -$RunAsUserScript = "runAsUser.ps1" -$CleanupScript = "cleanup.ps1" -$RunAsUserTask = "DevBoxCustomizations" -$CleanupTask = "DevBoxCustomizationsCleanup" - -# Set the progress preference to silently continue -# in order to avoid progress bars in the output -# as this makes web requests very slow -# Reference: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables -$ProgressPreference = 'SilentlyContinue' - -Start-Transcript -Path $env:TEMP\scheduled-task-customization.log -Append -IncludeInvocationHeader - -Write-Host "Microsoft Dev Box - Customizations" -Write-Host "----------------------------------" -Write-Host "Setting up scheduled tasks..." - -Remove-Item -Path "$($CustomizationScriptsDir)\$($LockFile)" - -Write-Host "Updating WinGet" -# ensure NuGet provider is installed -if (!(Get-PackageProvider | Where-Object { $_.Name -eq "NuGet" -and $_.Version -gt "2.8.5.201" })) { - Write-Host "Installing NuGet provider" - Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser - Write-Host "Done Installing NuGet provider" -} -else { - Write-Host "NuGet provider is already installed" -} - -# Set PSGallery installation policy to trusted -Set-PSRepository -Name "PSGallery" -InstallationPolicy Trusted - -# check if the Microsoft.Winget.Client module is installed -if (!(Get-Module -ListAvailable -Name Microsoft.Winget.Client)) { - Write-Host "Installing Microsoft.Winget.Client" - Install-Module Microsoft.WinGet.Client -Scope CurrentUser - Write-Host "Done Installing Microsoft.Winget.Client" -} -else { - Write-Host "Microsoft.Winget.Client is already installed" -} - -# check if the Microsoft.WinGet.Configuration module is installed -if (!(Get-Module -ListAvailable -Name Microsoft.WinGet.Configuration)) { - Write-Host "Installing Microsoft.WinGet.Configuration" - pwsh.exe -MTA -Command "Install-Module Microsoft.WinGet.Configuration -AllowPrerelease -Scope CurrentUser" - Write-Host "Done Installing Microsoft.WinGet.Configuration" -} -else { - Write-Host "Microsoft.WinGet.Configuration is already installed" -} - -$architecture = "x64" -if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") { - $architecture = "arm64" -} - -$msVCLibsPackage = Get-AppxPackage -Name "Microsoft.VCLibs.140.00.UWPDesktop" | Where-Object { $_.Version -ge "14.0.30035.0" } -if (!($msVCLibsPackage)) { -# Install Microsoft.VCLibs.140.00.UWPDesktop - try { - Write-Host "Installing Microsoft.VCLibs.140.00.UWPDesktop" - $MsVCLibs = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-Microsoft.VCLibs.140.00.UWPDesktop" - $MsVCLibsAppx = "$($MsVCLibs).appx" - - Invoke-WebRequest -Uri "https://aka.ms/Microsoft.VCLibs.$($architecture).14.00.Desktop.appx" -OutFile $MsVCLibsAppx - Add-AppxPackage -Path $MsVCLibsAppx -ForceApplicationShutdown - Write-Host "Done Installing Microsoft.VCLibs.140.00.UWPDesktop" - } catch { - Write-Host "Failed to install Microsoft.VCLibs.140.00.UWPDesktop" - Write-Error $_ - } -} - -$msUiXamlPackage = Get-AppxPackage -Name "Microsoft.UI.Xaml.2.8" | Where-Object { $_.Version -ge "8.2310.30001.0" } -if (!($msUiXamlPackage)) { - # instal Microsoft.UI.Xaml - try{ - Write-Host "Installing Microsoft.UI.Xaml" - $MsUiXaml = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-Microsoft.UI.Xaml.2.8.6" - $MsUiXamlZip = "$($MsUiXaml).zip" - Invoke-WebRequest -Uri "https://www.nuget.org/api/v2/package/Microsoft.UI.Xaml/2.8.6" -OutFile $MsUiXamlZip - Expand-Archive $MsUiXamlZip -DestinationPath $MsUiXaml - Add-AppxPackage -Path "$($MsUiXaml)\tools\AppX\$($architecture)\Release\Microsoft.UI.Xaml.2.8.appx" -ForceApplicationShutdown - Write-Host "Done Installing Microsoft.UI.Xaml" - } catch { - Write-Host "Failed to install Microsoft.UI.Xaml" - Write-Error $_ - } -} - -$desktopAppInstallerPackage = Get-AppxPackage -Name "Microsoft.DesktopAppInstaller" -if (!($desktopAppInstallerPackage) -or ($desktopAppInstallerPackage.Version -lt "1.22.0.0")) { - # install Microsoft.DesktopAppInstaller - try { - Write-Host "Installing Microsoft.DesktopAppInstaller" - $DesktopAppInstallerAppx = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-DesktopAppInstaller.appx" - Invoke-WebRequest -Uri "https://aka.ms/getwinget" -OutFile $DesktopAppInstallerAppx - - # install the DesktopAppInstaller appx package - Add-AppxPackage -Path $DesktopAppInstallerAppx -ForceApplicationShutdown - - Write-Host "Done Installing Microsoft.DesktopAppInstaller" - } - catch { - Write-Host "Failed to install DesktopAppInstaller appx package" - Write-Error $_ - } -} - -Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe -Write-Host "WinGet version: $(winget -v)" - -$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") -Write-Host "Done Updating WinGet" - -# Revert PSGallery installation policy to untrusted -Set-PSRepository -Name "PSGallery" -InstallationPolicy Untrusted - - diff --git a/.configuration/devcenter/tasks/winget/task.yaml b/.configuration/devcenter/tasks/winget/task.yaml deleted file mode 100644 index 3412c284..00000000 --- a/.configuration/devcenter/tasks/winget/task.yaml +++ /dev/null @@ -1,64 +0,0 @@ -# This is a winget package installation task for Dev Box. - -$schema: 1.0 -name: winget -description: Applies a winget configuration to the Dev Box. -author: Microsoft Corporation -command: .\main.ps1 -ConfigurationFile {{configurationFile}} -DownloadUrl {{downloadUrl}} -InlineConfigurationBase64 {{inlineConfigurationBase64}} -Package {{package}} -Version {{version}} -RunAsUser {{runAsUser}} -parameters: - configurationFile: - default: '' - type: 'string' - required: false - description: | - The path to the winget config yaml file. The file must be located in the local machine. - downloadUrl: - default: '' - type: 'string' - required: false - description: | - A publicly accessible URL where the config yaml file is stored. This file will be downloaded to the path given in 'configurationFile'. - inlineConfigurationBase64: - default: '' - type: 'string' - required: false - description: | - A base64 encoded string of the winget config yaml file. This file will be decoded and saved to the path given in 'configurationFile' or to a temporary file if not specified. - package: - default: '' - type: 'string' - required: false - description: | - The name of the package to install. This is an alternative way. - If a config yaml file is provided under other parameters, there is no need for the package name. - version: - default: '' - type: 'string' - required: false - description: | - The version of the package to install. - If a config yaml file is provided under other parameters, there is no need for the package version. - runAsUser: - default: false - type: 'boolean' - required: false - description: | - Whether to run the installation as the current user or as an administrator. - If set to true, the installation will run during the user's first login to the machine. -documentation: - notes: This task allows installing packages via winget, or applying a winget configuration file. - examples: - - name: winget - description: Install a package via winget - parameters: - package: git - - name: winget - description: Apply a winget configuration file that's already present on the machine - parameters: - configurationFile: 'C:\WinGetConfig\winget.yaml' - - name: winget - description: Apply a winget configuration file, downloading from a public URL to the specified path, running as the user - parameters: - downloadUrl: 'https://raw.githubusercontent.com/microsoft/devhome/main/sampleConfigurations/microsoft/vscode/configuration.dsc.yaml' - configurationFile: 'C:\WinGetConfig\configuration.dsc.yaml' - runAsUser: true diff --git a/.configuration/devcenter/workloads/ADO/common-backend-config.dsc.yaml b/.configuration/devcenter/workloads/ADO/common-backend-config.dsc.yaml index 79ba6fc2..26e6fc61 100644 --- a/.configuration/devcenter/workloads/ADO/common-backend-config.dsc.yaml +++ b/.configuration/devcenter/workloads/ADO/common-backend-config.dsc.yaml @@ -1,195 +1,195 @@ -# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 -# -# Backend Development Configuration Sample for .NET Engineers -# ============================================= -# -# Purpose: -# This DSC configuration installs essential tools for Azure backend development including: -# - Source control tools (GitHub Desktop) -# - Azure command-line tools (CLI, AZD, Bicep) -# - Local development emulators for Azure services -# -# Best Practices: -# - All tools are installed via WinGet for consistent versioning -# - Dependencies are explicitly declared to ensure proper installation order -# - Tools are grouped logically by purpose -# - Configuration should be version controlled and tested in CI/CD pipelines -# - Regular updates to ensure latest security patches and features -# - Separation of development tools from production configurations -# -properties: - configurationVersion: "0.2.0" - resources: - #---------------------------------------------- - # Azure Command-Line Tools - #---------------------------------------------- - # Resource: Azure CLI - # Azure CLI is the foundation for Azure management and serves as a dependency for other Azure tools. - # It provides command-line access to nearly all Azure service operations. - # - # Key Azure integration points: - # - Unified authentication with Microsoft Entra ID (formerly Azure AD) - # - Support for service principals and managed identities - # - JSON-based output for automation and scripting - # - Cross-platform compatibility for consistent workflows - # - # Security best practices: - # - Use 'az login --tenant' to explicitly specify tenants - # - Leverage managed identities where available - # - Apply RBAC with principle of least privilege - # - Use service principals with certificate-based auth for automation - # - Regularly update CLI using 'az upgrade' command - # - Avoid storing credentials in CLI cache in shared environments - # - Configure CLI to use your organization's approved proxy if required - # - # Common development scenarios: - # - Resource provisioning via templates and scripts - # - Querying resource status and configurations - # - Integrated deployment workflows with CI/CD - # - Management of secrets and connection strings - # - # DSC-specific notes: - # - WinGet ensures automatic updates to secure versions - # - Installation is idempotent and will not reinstall if present - # - Consider adding environment PATH validation during configuration testing - # - # Reference: https://learn.microsoft.com/en-us/cli/azure/ - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Microsoft.AzureCLI - directives: - allowPrerelease: true - description: Install Azure CLI for managing Azure resources from the command line - settings: - id: Microsoft.AzureCLI - - # Resource: Azure Developer CLI (azd) - # Azure Developer CLI (azd) simplifies application development workflow with templates - # and integrated deployment capabilities. - # - # Key Azure integration points: - # - End-to-end application development lifecycle management - # - Built-in templates for common Azure architectural patterns - # - Automated environment provisioning with infrastructure as code - # - Integration with GitHub Actions and Azure DevOps for CI/CD - # - Application monitoring and logging setup - # - # Development best practices: - # - Use environment variables for secrets ('azd env set') - # - Leverage service templates for consistent architecture - # - Implement standardized application structures - # - Follow Azure landing zone principles for environments - # - Store azd environment configurations in version control - # - Use azd pipeline integration for repeatable deployments - # - Include .env.template file but exclude .env files from source control - # - # Common development scenarios: - # - Setting up complete development environments - # - Implementing production-ready services with best practices - # - Consistent local-to-cloud development experience - # - Orchestrating multi-service deployments - # - # DSC-specific notes: - # - Dependency on Azure CLI is explicitly declared - # - Installation validates presence of required prerequisites - # - Consider adding post-install validation of azd version - # - # Reference: https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/ - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Microsoft.Azd - directives: - allowPrerelease: true - description: Install Azure Developer CLI (azd) for end-to-end application development - settings: - id: Microsoft.Azd - dependsOn: - - Microsoft.AzureCLI # AZD requires Azure CLI to function properly - - # Resource: Bicep CLI - # Bicep provides a domain-specific language for deploying Azure resources - # with improved syntax over ARM templates. - # - # Key Azure integration points: - # - Native integration with Azure Resource Manager - # - Support for all Azure resource types and apiVersions - # - Resource visualization capabilities - # - Module composition for reusable infrastructure - # - Built-in functions for dynamic deployments - # - # IaC best practices: - # - Use modules for reusable components - # - Implement parameterization for environment flexibility - # - Apply Azure Policy as Code for governance - # - Use symbolic references instead of string manipulation - # - Implement deployment validation with 'what-if' - # - Structure Bicep modules with clear separation of concerns - # - Validate Bicep files with 'bicep build' before deployment - # - Use linting tools to enforce conventions and best practices - # - Test deployments in isolation before integrating - # - # Common development scenarios: - # - Defining infrastructure as code for Azure environments - # - Creating reusable infrastructure modules - # - Setting up complex multi-resource deployments - # - Implementing infrastructure governance - # - # DSC-specific notes: - # - Bicep CLI is installed via Azure CLI extension - # - Installation updates to latest version automatically - # - Consider adding VS Code extension for Bicep in developer environments - # - # Reference: https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/ - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Microsoft.Bicep - directives: - allowPrerelease: true - description: Install Bicep CLI for Infrastructure as Code development on Azure - settings: - id: Microsoft.Bicep - dependsOn: - - Microsoft.AzureCLI # Bicep extensions use Azure CLI for deployment - - #---------------------------------------------- - # Local Development Emulators - #---------------------------------------------- - # Resource: Azure Storage Emulator - # Azure Storage Emulator provides a local environment for testing Azure Storage applications - # without requiring an Azure subscription for development. - # - # Key Azure integration points: - # - Local emulation of Blob, Queue, and Table storage - # - Development connection string compatibility with Azure Storage - # - Support for Azure Storage SDK integration - # - Local debugging of storage-dependent applications - # - Reduced development costs by minimizing cloud resource usage - # - # Development best practices: - # - Use consistent connection strings between local and cloud - # - Validate local operations match cloud behavior - # - Implement proper exception handling for both environments - # - Test with both emulator and actual Azure resources before deployment - # - Create automated tests that work with both environments - # - Consider using Azurite (newer emulator) for feature parity - # - Configure proper data persistence for development data - # - Document how to initialize the emulator in your project - # - # Common development scenarios: - # - Building applications using Azure Storage - # - Performing rapid iterative development - # - Unit and integration testing without cloud dependencies - # - Offline development scenarios - # - Cost optimization during development phases - # - # DSC-specific notes: - # - Installation may require administrator privileges - # - Consider validating emulator is running after installation - # - May need to configure firewall exceptions - # - Check SQL Server dependency is met (LocalDB) - # - # Reference: https://learn.microsoft.com/en-us/azure/storage/common/storage-use-emulator - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Microsoft.Azure.StorageEmulator - directives: - allowPrerelease: true - description: Install Azure Storage Emulator for local development - settings: - id: Microsoft.Azure.StorageEmulator +# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 +# +# Backend Development Configuration Sample for .NET Engineers +# ============================================= +# +# Purpose: +# This DSC configuration installs essential tools for Azure backend development including: +# - Source control tools (GitHub Desktop) +# - Azure command-line tools (CLI, AZD, Bicep) +# - Local development emulators for Azure services +# +# Best Practices: +# - All tools are installed via WinGet for consistent versioning +# - Dependencies are explicitly declared to ensure proper installation order +# - Tools are grouped logically by purpose +# - Configuration should be version controlled and tested in CI/CD pipelines +# - Regular updates to ensure latest security patches and features +# - Separation of development tools from production configurations +# +properties: + configurationVersion: "0.2.0" + resources: + #---------------------------------------------- + # Azure Command-Line Tools + #---------------------------------------------- + # Resource: Azure CLI + # Azure CLI is the foundation for Azure management and serves as a dependency for other Azure tools. + # It provides command-line access to nearly all Azure service operations. + # + # Key Azure integration points: + # - Unified authentication with Microsoft Entra ID (formerly Azure AD) + # - Support for service principals and managed identities + # - JSON-based output for automation and scripting + # - Cross-platform compatibility for consistent workflows + # + # Security best practices: + # - Use 'az login --tenant' to explicitly specify tenants + # - Leverage managed identities where available + # - Apply RBAC with principle of least privilege + # - Use service principals with certificate-based auth for automation + # - Regularly update CLI using 'az upgrade' command + # - Avoid storing credentials in CLI cache in shared environments + # - Configure CLI to use your organization's approved proxy if required + # + # Common development scenarios: + # - Resource provisioning via templates and scripts + # - Querying resource status and configurations + # - Integrated deployment workflows with CI/CD + # - Management of secrets and connection strings + # + # DSC-specific notes: + # - WinGet ensures automatic updates to secure versions + # - Installation is idempotent and will not reinstall if present + # - Consider adding environment PATH validation during configuration testing + # + # Reference: https://learn.microsoft.com/en-us/cli/azure/ + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Microsoft.AzureCLI + directives: + allowPrerelease: true + description: Install Azure CLI for managing Azure resources from the command line + settings: + id: Microsoft.AzureCLI + + # Resource: Azure Developer CLI (azd) + # Azure Developer CLI (azd) simplifies application development workflow with templates + # and integrated deployment capabilities. + # + # Key Azure integration points: + # - End-to-end application development lifecycle management + # - Built-in templates for common Azure architectural patterns + # - Automated environment provisioning with infrastructure as code + # - Integration with GitHub Actions and Azure DevOps for CI/CD + # - Application monitoring and logging setup + # + # Development best practices: + # - Use environment variables for secrets ('azd env set') + # - Leverage service templates for consistent architecture + # - Implement standardized application structures + # - Follow Azure landing zone principles for environments + # - Store azd environment configurations in version control + # - Use azd pipeline integration for repeatable deployments + # - Include .env.template file but exclude .env files from source control + # + # Common development scenarios: + # - Setting up complete development environments + # - Implementing production-ready services with best practices + # - Consistent local-to-cloud development experience + # - Orchestrating multi-service deployments + # + # DSC-specific notes: + # - Dependency on Azure CLI is explicitly declared + # - Installation validates presence of required prerequisites + # - Consider adding post-install validation of azd version + # + # Reference: https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/ + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Microsoft.Azd + directives: + allowPrerelease: true + description: Install Azure Developer CLI (azd) for end-to-end application development + settings: + id: Microsoft.Azd + dependsOn: + - Microsoft.AzureCLI # AZD requires Azure CLI to function properly + + # Resource: Bicep CLI + # Bicep provides a domain-specific language for deploying Azure resources + # with improved syntax over ARM templates. + # + # Key Azure integration points: + # - Native integration with Azure Resource Manager + # - Support for all Azure resource types and apiVersions + # - Resource visualization capabilities + # - Module composition for reusable infrastructure + # - Built-in functions for dynamic deployments + # + # IaC best practices: + # - Use modules for reusable components + # - Implement parameterization for environment flexibility + # - Apply Azure Policy as Code for governance + # - Use symbolic references instead of string manipulation + # - Implement deployment validation with 'what-if' + # - Structure Bicep modules with clear separation of concerns + # - Validate Bicep files with 'bicep build' before deployment + # - Use linting tools to enforce conventions and best practices + # - Test deployments in isolation before integrating + # + # Common development scenarios: + # - Defining infrastructure as code for Azure environments + # - Creating reusable infrastructure modules + # - Setting up complex multi-resource deployments + # - Implementing infrastructure governance + # + # DSC-specific notes: + # - Bicep CLI is installed via Azure CLI extension + # - Installation updates to latest version automatically + # - Consider adding VS Code extension for Bicep in developer environments + # + # Reference: https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/ + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Microsoft.Bicep + directives: + allowPrerelease: true + description: Install Bicep CLI for Infrastructure as Code development on Azure + settings: + id: Microsoft.Bicep + dependsOn: + - Microsoft.AzureCLI # Bicep extensions use Azure CLI for deployment + + #---------------------------------------------- + # Local Development Emulators + #---------------------------------------------- + # Resource: Azure Storage Emulator + # Azure Storage Emulator provides a local environment for testing Azure Storage applications + # without requiring an Azure subscription for development. + # + # Key Azure integration points: + # - Local emulation of Blob, Queue, and Table storage + # - Development connection string compatibility with Azure Storage + # - Support for Azure Storage SDK integration + # - Local debugging of storage-dependent applications + # - Reduced development costs by minimizing cloud resource usage + # + # Development best practices: + # - Use consistent connection strings between local and cloud + # - Validate local operations match cloud behavior + # - Implement proper exception handling for both environments + # - Test with both emulator and actual Azure resources before deployment + # - Create automated tests that work with both environments + # - Consider using Azurite (newer emulator) for feature parity + # - Configure proper data persistence for development data + # - Document how to initialize the emulator in your project + # + # Common development scenarios: + # - Building applications using Azure Storage + # - Performing rapid iterative development + # - Unit and integration testing without cloud dependencies + # - Offline development scenarios + # - Cost optimization during development phases + # + # DSC-specific notes: + # - Installation may require administrator privileges + # - Consider validating emulator is running after installation + # - May need to configure firewall exceptions + # - Check SQL Server dependency is met (LocalDB) + # + # Reference: https://learn.microsoft.com/en-us/azure/storage/common/storage-use-emulator + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Microsoft.Azure.StorageEmulator + directives: + allowPrerelease: true + description: Install Azure Storage Emulator for local development + settings: + id: Microsoft.Azure.StorageEmulator diff --git a/.configuration/devcenter/workloads/ADO/common-backend-usertasks-config.dsc.yaml b/.configuration/devcenter/workloads/ADO/common-backend-usertasks-config.dsc.yaml index 394dbe7e..82c86bf6 100644 --- a/.configuration/devcenter/workloads/ADO/common-backend-usertasks-config.dsc.yaml +++ b/.configuration/devcenter/workloads/ADO/common-backend-usertasks-config.dsc.yaml @@ -1,333 +1,333 @@ -# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 -# -# Backend Development Configuration Sample for .NET Engineers -# ============================================= -# -# Purpose: -# This DSC configuration creates a complete Azure backend development environment with: -# - WSL2 with Ubuntu for Linux-based development -# - Azure CLI tools in both Windows and Ubuntu environments -# - .NET development tools in both environments -# - Container tools (Docker Desktop, Helm) for microservice development -# - API testing tools (Postman) -# -# Requirements: -# - with Windows 11 -# - Administrator access for tool installation -# - Internet connectivity for package downloads -# -# Security Considerations: -# - Uses secure communication channels for downloads -# - Applies principle of least privilege where possible -# - Uses official Microsoft installation methods -# -# Best Practices: -# - Uses Windows and WSL2 for a complete cross-platform experience -# - Applies principle of least privilege where possible -# - Maintains consistent environment between Windows and Linux -# - Uses official Microsoft installation methods for all tools -# -# Maintainer: DevExp Team -# -properties: - configurationVersion: 0.2.0 - - resources: - #---------------------------------------------- - # WSL2 Installation and Configuration - #---------------------------------------------- - # Windows Subsystem for Linux is the foundation for cross-platform development - # enabling Linux-based Azure services testing in a local environment - - resource: PSDscResources/Script - id: Microsoft-Windows-Subsystem-Linux - directives: - description: Enable Windows Features and Install Ubuntu for WSL - securityContext: elevated # Requires admin rights to enable Windows features - settings: - SetScript: | - try { - # Set execution policy for current process - Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue - # Ensure path is properly set - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") - Write-Verbose "Installing Windows Subsystem for Linux (WSL)" -Verbose - wsl --install --no-launch 2>$null - } catch { - Write-Error "Failed to install WSL: $_" - } - GetScript: return $false - TestScript: return $false - - # Install Ubuntu distribution which provides a comprehensive Linux environment - # with broad Azure service compatibility and tool support - - resource: PSDscResources/Script - id: Ubuntu - directives: - description: Install Ubuntu for WSL - securityContext: elevated # Requires admin rights to install WSL distribution - settings: - SetScript: | - try { - # Set execution policy for current process - Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue - # Ensure path is properly set - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") - Write-Verbose "Installing Ubuntu for WSL" -Verbose - wsl --install -d Ubuntu --no-launch 2>$null - } catch { - Write-Error "Failed to install Ubuntu: $_" - } - GetScript: return $false - TestScript: return $false - - # Configure user access to simplify development and avoid credential prompts - # while maintaining security through Windows authentication - - resource: PSDscResources/Script - id: Ubuntu.User - directives: - description: Add the current user as an administrator to Ubuntu - securityContext: elevated # Requires admin rights to configure WSL distribution - settings: - SetScript: | - try { - # Set execution policy for current process - Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue - # Ensure path is properly set - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") - $newUser = $env:USERNAME.ToLower() - $password = "SecureP@ssw0rd" # Consider using a more secure password generation method - $escapedPassword = $password -replace '(["\\$`])', '\\$1' - # Create user account - wsl.exe -d Ubuntu -u root -- bash -c "sudo adduser --quiet --disabled-password --gecos '' $newUser" - # Set password - wsl.exe -d Ubuntu -u root -- bash -c "echo '${newUser}:${escapedPassword}' | sudo chpasswd" - # Add to sudo group - wsl.exe -d Ubuntu -u root -- bash -c "sudo usermod -aG sudo $newUser" - # Enable passwordless sudo for DevBox convenience - wsl.exe -d Ubuntu -u root -- bash -c "echo '$newUser ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/$newUser" - } catch { - Write-Error "Failed to add user as administrator: $_" - } - GetScript: return $false - TestScript: return $false - - # Update Ubuntu packages to ensure security patches and latest tool compatibility - # following Azure best practices for keeping development environments current - - resource: PSDscResources/Script - id: Ubuntu.Update - directives: - description: Update Ubuntu package repositories and install updates - securityContext: elevated # Requires admin rights to run WSL commands - settings: - SetScript: | - try { - # Set execution policy for current process - Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue - # Ensure path is properly set - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") - $updateCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; apt-get update && apt-get upgrade -y --quiet'" - wsl.exe -d Ubuntu -u root -- bash -c "$updateCommand" - } catch { - Write-Error "Failed to update Ubuntu packages: $_" - } - GetScript: return $false - TestScript: return $false - - #---------------------------------------------- - # Azure Development Tools for Ubuntu - #---------------------------------------------- - # Install Azure CLI tools in Ubuntu to provide cross-platform development capability - # and ensure consistent deployment behavior between local and cloud environments - - resource: PSDscResources/Script - id: Ubuntu.AzureCli - directives: - description: Install Azure CLI, Developer CLI and Bicep on Ubuntu - securityContext: elevated # Requires admin rights to run WSL commands - settings: - SetScript: | - try { - # Set execution policy for current process - Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue - # Ensure path is properly set - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") - # Install curl as prerequisite - $updateCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; apt-get install curl --quiet -y'" - wsl.exe -d Ubuntu -u root -- bash -c "$updateCommand" - # Install Azure CLI using official Microsoft script - $updateCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; curl -fsSL https://aka.ms/InstallAzureCLIDeb | sudo bash'" - wsl.exe -d Ubuntu -u root -- bash -c "$updateCommand" - # Install Azure Developer CLI using official Microsoft script - $updateCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; curl -fsSL https://aka.ms/install-azd.sh | sudo bash'" - wsl.exe -d Ubuntu -u root -- bash -c "$updateCommand" - } catch { - Write-Error "Failed to install Azure CLI: $_" - } - GetScript: return $false - TestScript: return $false - - # Install .NET SDK and Runtime for cross-platform .NET development - # enabling development of Azure Functions, Web Apps, and microservices - - resource: PSDscResources/Script - id: Ubuntu.DotNetSDK - directives: - description: Install .NET 9 SDK and Runtime on Ubuntu - securityContext: elevated # Requires admin rights to run WSL commands - settings: - SetScript: | - try { - # Set execution policy for current process - Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue - # Ensure path is properly set - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") - # Add Microsoft's PPA for .NET - $installDotnetCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; sudo add-apt-repository ppa:dotnet/backports -y'" - wsl.exe -d Ubuntu -u root -- bash -c "$installDotnetCommand" - # Install .NET SDK for development - $installDotnetCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; apt-get update --quiet && apt-get install --quiet -y dotnet-sdk-9.0'" - wsl.exe -d Ubuntu -u root -- bash -c "$installDotnetCommand" - # Install ASP.NET Core Runtime for web applications - $installDotnetCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; apt-get install --quiet -y aspnetcore-runtime-9.0'" - wsl.exe -d Ubuntu -u root -- bash -c "$installDotnetCommand" - } catch { - Write-Error "Failed to install .NET SDK and Runtime: $_" - } - GetScript: return $false - TestScript: return $false - - #---------------------------------------------- - # Container and API Development Tools - #---------------------------------------------- - - # Resource: Docker Desktop - # Docker Desktop provides container management with Kubernetes integration - # essential for microservices development and container-based Azure services. - # - # This tool enables: - # - Local testing of containerized applications before Azure deployment - # - Integration with Azure Container Registry for image management - # - Local Kubernetes testing before AKS deployment - # - Development of container-based solutions for Azure App Service, ACI, and AKS - # - # Following Azure best practices by using the official package through WinGet. - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Docker.DockerDesktop - directives: - description: Install Docker Desktop for container development and testing - allowPrerelease: true - securityContext: elevated # Requires admin rights to install system services - settings: - id: Docker.DockerDesktop - - # Resource: Helm Package Manager - # Helm simplifies Kubernetes application management with package-based deployments - # commonly used with Azure Kubernetes Service (AKS). - # - # This tool enables: - # - Consistent deployment of applications to AKS - # - Management of application release cycles - # - Simplified configuration of complex Kubernetes applications - # - Integration with Azure DevOps pipelines for CI/CD - # - # WinGet installation ensures the official package is used. - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Helm.Helm - directives: - description: Install Helm for Kubernetes application deployment management - allowPrerelease: true - settings: - id: Helm.Helm - - # Resource: Postman API Testing Platform - # Postman enables API testing and documentation, ensuring reliable integrations - # between backend services and Azure APIs. - # - # This tool enables: - # - Testing of Azure Functions, Logic Apps, and API Management endpoints - # - Creation of comprehensive API test suites - # - Documentation of APIs for development teams - # - Automation of API testing for Azure integration scenarios - # - # WinGet installation follows Azure security best practices. - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Postman.Postman - directives: - description: Install Postman API platform for designing, testing and documenting APIs - allowPrerelease: true - settings: - id: Postman.Postman - - # Resource: Visual Studio Code Extensions - # This resource installs essential VS Code extensions for Azure backend development. - # The extensions provide IntelliSense, debugging, and other features specific to - # Azure development workflows, ensuring a complete development environment. - # - # These extensions follow the Microsoft-recommended developer tooling for - # Azure cloud-native application development and DevOps practices. - - resource: PSDscResources/Script - id: Microsoft.VisualStudioCode.Extensions - directives: - description: Install VS Code Extensions for Azure backend development - securityContext: elevated # Required for system-wide extension installation - allowPrerelease: true # Allows latest preview features - settings: - SetScript: | - try { - # Set execution policy to bypass for automation purposes - # This is required for PowerShell script execution in restricted environments - Set-ExecutionPolicy Bypass -Scope Process -Force - - # Ensure VS Code is in the PATH for the code command - # Combines machine and user paths to ensure the command is found - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") - - Write-Verbose "Installing VS Code extensions for Azure backend development..." -Verbose - - # PowerShell extension for scripting and Azure automation tasks - code --install-extension ms-vscode.powershell - - # WSL integration for cross-platform development between Windows/Linux - code --install-extension ms-vscode-remote.remote-wsl - - # C# development kit for .NET applications and Azure Functions - code --install-extension ms-dotnettools.csdevkit - - # TypeScript with latest features for modern API development - code --install-extension ms-vscode.vscode-typescript-next - - # YAML support for Kubernetes, ARM templates, and pipelines - code --install-extension redhat.vscode-yaml - - # Bicep for Azure infrastructure as code (recommended over ARM templates) - code --install-extension ms-azuretools.vscode-bicep - - # Azure Tools extension pack for comprehensive Azure development - code --install-extension ms-vscode.vscode-node-azure-pack - - # Azure CLI tools for command-line management of Azure resources - code --install-extension ms-vscode.azurecli - - # GitHub remote repositories for cloud-based development - code --install-extension GitHub.remotehub - - # GitHub Pull Requests for collaborative development - code --install-extension GitHub.vscode-pull-request-github - - # Docker extension for container management and development - code --install-extension ms-azuretools.vscode-docker - - # Kubernetes extension for managing Kubernetes clusters and resources - code --install-extension ms-kubernetes-tools.vscode-kubernetes-tools - - # Postman extension for API testing and documentation - code --install-extension Postman.postman-for-vscode - - Write-Verbose "VS Code extensions installed successfully" -Verbose - } catch { - Write-Error "Failed to install VS Code extensions: $_" - } - # In a fully idempotent implementation, this would return the current state - # of installed extensions for reporting and validation - GetScript: return $false - # In production environments, TestScript would check if specific - # extensions are already installed to avoid unnecessary reinstallation - TestScript: return $false +# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 +# +# Backend Development Configuration Sample for .NET Engineers +# ============================================= +# +# Purpose: +# This DSC configuration creates a complete Azure backend development environment with: +# - WSL2 with Ubuntu for Linux-based development +# - Azure CLI tools in both Windows and Ubuntu environments +# - .NET development tools in both environments +# - Container tools (Docker Desktop, Helm) for microservice development +# - API testing tools (Postman) +# +# Requirements: +# - with Windows 11 +# - Administrator access for tool installation +# - Internet connectivity for package downloads +# +# Security Considerations: +# - Uses secure communication channels for downloads +# - Applies principle of least privilege where possible +# - Uses official Microsoft installation methods +# +# Best Practices: +# - Uses Windows and WSL2 for a complete cross-platform experience +# - Applies principle of least privilege where possible +# - Maintains consistent environment between Windows and Linux +# - Uses official Microsoft installation methods for all tools +# +# Maintainer: DevExp Team +# +properties: + configurationVersion: 0.2.0 + + resources: + #---------------------------------------------- + # WSL2 Installation and Configuration + #---------------------------------------------- + # Windows Subsystem for Linux is the foundation for cross-platform development + # enabling Linux-based Azure services testing in a local environment + - resource: PSDscResources/Script + id: Microsoft-Windows-Subsystem-Linux + directives: + description: Enable Windows Features and Install Ubuntu for WSL + securityContext: elevated # Requires admin rights to enable Windows features + settings: + SetScript: | + try { + # Set execution policy for current process + Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue + # Ensure path is properly set + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + Write-Verbose "Installing Windows Subsystem for Linux (WSL)" -Verbose + wsl --install --no-launch 2>$null + } catch { + Write-Error "Failed to install WSL: $_" + } + GetScript: return $false + TestScript: return $false + + # Install Ubuntu distribution which provides a comprehensive Linux environment + # with broad Azure service compatibility and tool support + - resource: PSDscResources/Script + id: Ubuntu + directives: + description: Install Ubuntu for WSL + securityContext: elevated # Requires admin rights to install WSL distribution + settings: + SetScript: | + try { + # Set execution policy for current process + Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue + # Ensure path is properly set + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + Write-Verbose "Installing Ubuntu for WSL" -Verbose + wsl --install -d Ubuntu --no-launch 2>$null + } catch { + Write-Error "Failed to install Ubuntu: $_" + } + GetScript: return $false + TestScript: return $false + + # Configure user access to simplify development and avoid credential prompts + # while maintaining security through Windows authentication + - resource: PSDscResources/Script + id: Ubuntu.User + directives: + description: Add the current user as an administrator to Ubuntu + securityContext: elevated # Requires admin rights to configure WSL distribution + settings: + SetScript: | + try { + # Set execution policy for current process + Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue + # Ensure path is properly set + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + $newUser = $env:USERNAME.ToLower() + $password = "SecureP@ssw0rd" # Consider using a more secure password generation method + $escapedPassword = $password -replace '(["\\$`])', '\\$1' + # Create user account + wsl.exe -d Ubuntu -u root -- bash -c "sudo adduser --quiet --disabled-password --gecos '' $newUser" + # Set password + wsl.exe -d Ubuntu -u root -- bash -c "echo '${newUser}:${escapedPassword}' | sudo chpasswd" + # Add to sudo group + wsl.exe -d Ubuntu -u root -- bash -c "sudo usermod -aG sudo $newUser" + # Enable passwordless sudo for DevBox convenience + wsl.exe -d Ubuntu -u root -- bash -c "echo '$newUser ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/$newUser" + } catch { + Write-Error "Failed to add user as administrator: $_" + } + GetScript: return $false + TestScript: return $false + + # Update Ubuntu packages to ensure security patches and latest tool compatibility + # following Azure best practices for keeping development environments current + - resource: PSDscResources/Script + id: Ubuntu.Update + directives: + description: Update Ubuntu package repositories and install updates + securityContext: elevated # Requires admin rights to run WSL commands + settings: + SetScript: | + try { + # Set execution policy for current process + Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue + # Ensure path is properly set + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + $updateCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; apt-get update && apt-get upgrade -y --quiet'" + wsl.exe -d Ubuntu -u root -- bash -c "$updateCommand" + } catch { + Write-Error "Failed to update Ubuntu packages: $_" + } + GetScript: return $false + TestScript: return $false + + #---------------------------------------------- + # Azure Development Tools for Ubuntu + #---------------------------------------------- + # Install Azure CLI tools in Ubuntu to provide cross-platform development capability + # and ensure consistent deployment behavior between local and cloud environments + - resource: PSDscResources/Script + id: Ubuntu.AzureCli + directives: + description: Install Azure CLI, Developer CLI and Bicep on Ubuntu + securityContext: elevated # Requires admin rights to run WSL commands + settings: + SetScript: | + try { + # Set execution policy for current process + Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue + # Ensure path is properly set + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + # Install curl as prerequisite + $updateCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; apt-get install curl --quiet -y'" + wsl.exe -d Ubuntu -u root -- bash -c "$updateCommand" + # Install Azure CLI using official Microsoft script + $updateCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; curl -fsSL https://aka.ms/InstallAzureCLIDeb | sudo bash'" + wsl.exe -d Ubuntu -u root -- bash -c "$updateCommand" + # Install Azure Developer CLI using official Microsoft script + $updateCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; curl -fsSL https://aka.ms/install-azd.sh | sudo bash'" + wsl.exe -d Ubuntu -u root -- bash -c "$updateCommand" + } catch { + Write-Error "Failed to install Azure CLI: $_" + } + GetScript: return $false + TestScript: return $false + + # Install .NET SDK and Runtime for cross-platform .NET development + # enabling development of Azure Functions, Web Apps, and microservices + - resource: PSDscResources/Script + id: Ubuntu.DotNetSDK + directives: + description: Install .NET 9 SDK and Runtime on Ubuntu + securityContext: elevated # Requires admin rights to run WSL commands + settings: + SetScript: | + try { + # Set execution policy for current process + Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue + # Ensure path is properly set + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + # Add Microsoft's PPA for .NET + $installDotnetCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; sudo add-apt-repository ppa:dotnet/backports -y'" + wsl.exe -d Ubuntu -u root -- bash -c "$installDotnetCommand" + # Install .NET SDK for development + $installDotnetCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; apt-get update --quiet && apt-get install --quiet -y dotnet-sdk-9.0'" + wsl.exe -d Ubuntu -u root -- bash -c "$installDotnetCommand" + # Install ASP.NET Core Runtime for web applications + $installDotnetCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; apt-get install --quiet -y aspnetcore-runtime-9.0'" + wsl.exe -d Ubuntu -u root -- bash -c "$installDotnetCommand" + } catch { + Write-Error "Failed to install .NET SDK and Runtime: $_" + } + GetScript: return $false + TestScript: return $false + + #---------------------------------------------- + # Container and API Development Tools + #---------------------------------------------- + + # Resource: Docker Desktop + # Docker Desktop provides container management with Kubernetes integration + # essential for microservices development and container-based Azure services. + # + # This tool enables: + # - Local testing of containerized applications before Azure deployment + # - Integration with Azure Container Registry for image management + # - Local Kubernetes testing before AKS deployment + # - Development of container-based solutions for Azure App Service, ACI, and AKS + # + # Following Azure best practices by using the official package through WinGet. + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Docker.DockerDesktop + directives: + description: Install Docker Desktop for container development and testing + allowPrerelease: true + securityContext: elevated # Requires admin rights to install system services + settings: + id: Docker.DockerDesktop + + # Resource: Helm Package Manager + # Helm simplifies Kubernetes application management with package-based deployments + # commonly used with Azure Kubernetes Service (AKS). + # + # This tool enables: + # - Consistent deployment of applications to AKS + # - Management of application release cycles + # - Simplified configuration of complex Kubernetes applications + # - Integration with Azure DevOps pipelines for CI/CD + # + # WinGet installation ensures the official package is used. + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Helm.Helm + directives: + description: Install Helm for Kubernetes application deployment management + allowPrerelease: true + settings: + id: Helm.Helm + + # Resource: Postman API Testing Platform + # Postman enables API testing and documentation, ensuring reliable integrations + # between backend services and Azure APIs. + # + # This tool enables: + # - Testing of Azure Functions, Logic Apps, and API Management endpoints + # - Creation of comprehensive API test suites + # - Documentation of APIs for development teams + # - Automation of API testing for Azure integration scenarios + # + # WinGet installation follows Azure security best practices. + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Postman.Postman + directives: + description: Install Postman API platform for designing, testing and documenting APIs + allowPrerelease: true + settings: + id: Postman.Postman + + # Resource: Visual Studio Code Extensions + # This resource installs essential VS Code extensions for Azure backend development. + # The extensions provide IntelliSense, debugging, and other features specific to + # Azure development workflows, ensuring a complete development environment. + # + # These extensions follow the Microsoft-recommended developer tooling for + # Azure cloud-native application development and DevOps practices. + - resource: PSDscResources/Script + id: Microsoft.VisualStudioCode.Extensions + directives: + description: Install VS Code Extensions for Azure backend development + securityContext: elevated # Required for system-wide extension installation + allowPrerelease: true # Allows latest preview features + settings: + SetScript: | + try { + # Set execution policy to bypass for automation purposes + # This is required for PowerShell script execution in restricted environments + Set-ExecutionPolicy Bypass -Scope Process -Force + + # Ensure VS Code is in the PATH for the code command + # Combines machine and user paths to ensure the command is found + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + + Write-Verbose "Installing VS Code extensions for Azure backend development..." -Verbose + + # PowerShell extension for scripting and Azure automation tasks + code --install-extension ms-vscode.powershell + + # WSL integration for cross-platform development between Windows/Linux + code --install-extension ms-vscode-remote.remote-wsl + + # C# development kit for .NET applications and Azure Functions + code --install-extension ms-dotnettools.csdevkit + + # TypeScript with latest features for modern API development + code --install-extension ms-vscode.vscode-typescript-next + + # YAML support for Kubernetes, ARM templates, and pipelines + code --install-extension redhat.vscode-yaml + + # Bicep for Azure infrastructure as code (recommended over ARM templates) + code --install-extension ms-azuretools.vscode-bicep + + # Azure Tools extension pack for comprehensive Azure development + code --install-extension ms-vscode.vscode-node-azure-pack + + # Azure CLI tools for command-line management of Azure resources + code --install-extension ms-vscode.azurecli + + # GitHub remote repositories for cloud-based development + code --install-extension GitHub.remotehub + + # GitHub Pull Requests for collaborative development + code --install-extension GitHub.vscode-pull-request-github + + # Docker extension for container management and development + code --install-extension ms-azuretools.vscode-docker + + # Kubernetes extension for managing Kubernetes clusters and resources + code --install-extension ms-kubernetes-tools.vscode-kubernetes-tools + + # Postman extension for API testing and documentation + code --install-extension Postman.postman-for-vscode + + Write-Verbose "VS Code extensions installed successfully" -Verbose + } catch { + Write-Error "Failed to install VS Code extensions: $_" + } + # In a fully idempotent implementation, this would return the current state + # of installed extensions for reporting and validation + GetScript: return $false + # In production environments, TestScript would check if specific + # extensions are already installed to avoid unnecessary reinstallation + TestScript: return $false diff --git a/.configuration/devcenter/workloads/ADO/common-config.dsc.yaml b/.configuration/devcenter/workloads/ADO/common-config.dsc.yaml index a897ca8b..ac95889a 100644 --- a/.configuration/devcenter/workloads/ADO/common-config.dsc.yaml +++ b/.configuration/devcenter/workloads/ADO/common-config.dsc.yaml @@ -1,160 +1,160 @@ -# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 -# -# Common Configuration Sample for .NET Engineers -# ================================= -# -# Purpose: -# This DSC configuration sets up a standard development environment with: -# - Development storage using Dev Drive -# - Source control tools (Git, GitHub CLI) -# - Development runtimes (.NET 9 SDK and Runtime) -# - Development tools (VS Code, Node.js) -# -# Prerequisites: -# - Windows 10/11 with admin privileges -# - Internet connectivity for package downloads -# -# Maintainers: DevExp Team - -properties: - configurationVersion: "0.2.0" - resources: - #---------------------------------------------- - # Source Control Tools - #---------------------------------------------- - # Resource: Git - # Git is essential for workflows, enabling developers to work with - # source code repositories, CI/CD pipelines, and infrastructure as code. - # - # Azure-specific considerations: - # - Supports Azure DevOps repositories and GitHub integration - # - Required for Azure Bicep template development and versioning - # - Enables GitOps workflows with Azure Arc and Azure Kubernetes Service - # - Optimized when used with Dev Drive for performance - # - # Will be used by: - # - Azure CLI when working with Azure Repos - # - GitHub CLI for GitHub-hosted Azure projects - # - Azure Developer CLI (azd) for project templates - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Git.Git - directives: - allowPrerelease: true - description: Install Git version control system - settings: - id: Git.Git - - # Resource: GitHub CLI - # GitHub CLI streamlines GitHub workflow automation directly from the terminal, - # enabling efficient Azure DevOps integration and GitHub Actions management. - # - # Azure-specific benefits: - # - Manage GitHub repositories that host Azure infrastructure code - # - Create and manage GitHub Actions workflows for Azure deployments - # - Work with GitHub issues and pull requests for Azure service development - # - Authenticate to GitHub Container Registry for Azure container deployments - # - # Security note: - # - Authentication tokens are stored securely in Windows Credential Manager - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: GitHub.cli - directives: - allowPrerelease: true - description: Install GitHub command-line interface - settings: - id: GitHub.cli - dependsOn: - - Git.Git # Requires Git for full functionality - - #---------------------------------------------- - # Development Runtimes - #---------------------------------------------- - # Resource: .NET SDK 9 - # The .NET SDK is core to Azure development, providing the tools needed to build, - # test, and deploy applications targeting Azure services. - # - # Azure-specific features: - # - Azure SDK integration for Azure services - # - Built-in templates for Azure Functions and Web Apps - # - Support for containerized applications on Azure Container Apps - # - Tools for Azure Active Directory integration - # - Azure-optimized middleware components - # - # Performance note: - # - Benefits from Dev Drive configuration for large solution files - # - Includes .NET CLI tools for Azure Functions development - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Microsoft.DotNet.SDK.9 - directives: - allowPrerelease: true - description: Install .NET 9 SDK for application development - settings: - id: Microsoft.DotNet.SDK.9 - - # Resource: .NET Runtime 9 - # The .NET Runtime enables execution of .NET applications and is required - # for running Azure tooling that depends on .NET. - # - # Azure-specific considerations: - # - Required by many Azure command-line tools (Azure PowerShell, etc.) - # - Supports running Azure Functions core tools locally - # - Needed for Azure Storage Emulator and other local emulators - # - Enables testing of containerized .NET applications before Azure deployment - # - # Note: While included in the SDK, explicit installation ensures availability - # for tools that may require the runtime directly - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Microsoft.DotNet.Runtime.9 - directives: - allowPrerelease: true - description: Install .NET 9 Runtime - settings: - id: Microsoft.DotNet.Runtime.9 - dependsOn: - - Microsoft.DotNet.SDK.9 # Runtime is included in SDK, but explicit dependency ensures correct order - - # Resource: Node.js - # Node.js is a JavaScript runtime environment essential for modern web development - # and crucial for many Azure development scenarios. - # - # Azure-specific benefits: - # - Required for Azure Static Web Apps local development - # - Used by Azure Functions for JavaScript/TypeScript functions - # - Powers npm packages for Azure SDK for JavaScript - # - Enables local development of Azure App Service Node.js applications - # - Required for many Azure DevOps build pipelines - # - # Security note: - # - WinGet installation ensures secure, verified package source - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: OpenJS.NodeJS - directives: - allowPrerelease: true - description: Install Node.js JavaScript runtime - settings: - id: OpenJS.NodeJS - - #---------------------------------------------- - # Development Tools - #---------------------------------------------- - # Resource: Visual Studio Code - # VS Code is Microsoft's recommended editor for Azure development with - # extensive Azure service integration through extensions. - # - # Azure-specific capabilities: - # - Direct Azure resource management through Azure extensions - # - Integrated terminal for Azure CLI and PowerShell commands - # - Azure Functions local development and debugging - # - Azure App Service deployment integration - # - Cosmos DB explorer and Storage explorer integrations - # - ARM template and Bicep authoring and validation - # - # Note: Additional Azure extensions will be installed in separate configurations - # to provide targeted functionality for specific development scenarios - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Microsoft.VisualStudioCode - directives: - allowPrerelease: true - description: Install Visual Studio Code editor - settings: - id: Microsoft.VisualStudioCode +# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 +# +# Common Configuration Sample for .NET Engineers +# ================================= +# +# Purpose: +# This DSC configuration sets up a standard development environment with: +# - Development storage using Dev Drive +# - Source control tools (Git, GitHub CLI) +# - Development runtimes (.NET 9 SDK and Runtime) +# - Development tools (VS Code, Node.js) +# +# Prerequisites: +# - Windows 10/11 with admin privileges +# - Internet connectivity for package downloads +# +# Maintainers: DevExp Team + +properties: + configurationVersion: "0.2.0" + resources: + #---------------------------------------------- + # Source Control Tools + #---------------------------------------------- + # Resource: Git + # Git is essential for workflows, enabling developers to work with + # source code repositories, CI/CD pipelines, and infrastructure as code. + # + # Azure-specific considerations: + # - Supports Azure DevOps repositories and GitHub integration + # - Required for Azure Bicep template development and versioning + # - Enables GitOps workflows with Azure Arc and Azure Kubernetes Service + # - Optimized when used with Dev Drive for performance + # + # Will be used by: + # - Azure CLI when working with Azure Repos + # - GitHub CLI for GitHub-hosted Azure projects + # - Azure Developer CLI (azd) for project templates + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Git.Git + directives: + allowPrerelease: true + description: Install Git version control system + settings: + id: Git.Git + + # Resource: GitHub CLI + # GitHub CLI streamlines GitHub workflow automation directly from the terminal, + # enabling efficient Azure DevOps integration and GitHub Actions management. + # + # Azure-specific benefits: + # - Manage GitHub repositories that host Azure infrastructure code + # - Create and manage GitHub Actions workflows for Azure deployments + # - Work with GitHub issues and pull requests for Azure service development + # - Authenticate to GitHub Container Registry for Azure container deployments + # + # Security note: + # - Authentication tokens are stored securely in Windows Credential Manager + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: GitHub.cli + directives: + allowPrerelease: true + description: Install GitHub command-line interface + settings: + id: GitHub.cli + dependsOn: + - Git.Git # Requires Git for full functionality + + #---------------------------------------------- + # Development Runtimes + #---------------------------------------------- + # Resource: .NET SDK 9 + # The .NET SDK is core to Azure development, providing the tools needed to build, + # test, and deploy applications targeting Azure services. + # + # Azure-specific features: + # - Azure SDK integration for Azure services + # - Built-in templates for Azure Functions and Web Apps + # - Support for containerized applications on Azure Container Apps + # - Tools for Azure Active Directory integration + # - Azure-optimized middleware components + # + # Performance note: + # - Benefits from Dev Drive configuration for large solution files + # - Includes .NET CLI tools for Azure Functions development + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Microsoft.DotNet.SDK.9 + directives: + allowPrerelease: true + description: Install .NET 9 SDK for application development + settings: + id: Microsoft.DotNet.SDK.9 + + # Resource: .NET Runtime 9 + # The .NET Runtime enables execution of .NET applications and is required + # for running Azure tooling that depends on .NET. + # + # Azure-specific considerations: + # - Required by many Azure command-line tools (Azure PowerShell, etc.) + # - Supports running Azure Functions core tools locally + # - Needed for Azure Storage Emulator and other local emulators + # - Enables testing of containerized .NET applications before Azure deployment + # + # Note: While included in the SDK, explicit installation ensures availability + # for tools that may require the runtime directly + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Microsoft.DotNet.Runtime.9 + directives: + allowPrerelease: true + description: Install .NET 9 Runtime + settings: + id: Microsoft.DotNet.Runtime.9 + dependsOn: + - Microsoft.DotNet.SDK.9 # Runtime is included in SDK, but explicit dependency ensures correct order + + # Resource: Node.js + # Node.js is a JavaScript runtime environment essential for modern web development + # and crucial for many Azure development scenarios. + # + # Azure-specific benefits: + # - Required for Azure Static Web Apps local development + # - Used by Azure Functions for JavaScript/TypeScript functions + # - Powers npm packages for Azure SDK for JavaScript + # - Enables local development of Azure App Service Node.js applications + # - Required for many Azure DevOps build pipelines + # + # Security note: + # - WinGet installation ensures secure, verified package source + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: OpenJS.NodeJS + directives: + allowPrerelease: true + description: Install Node.js JavaScript runtime + settings: + id: OpenJS.NodeJS + + #---------------------------------------------- + # Development Tools + #---------------------------------------------- + # Resource: Visual Studio Code + # VS Code is Microsoft's recommended editor for Azure development with + # extensive Azure service integration through extensions. + # + # Azure-specific capabilities: + # - Direct Azure resource management through Azure extensions + # - Integrated terminal for Azure CLI and PowerShell commands + # - Azure Functions local development and debugging + # - Azure App Service deployment integration + # - Cosmos DB explorer and Storage explorer integrations + # - ARM template and Bicep authoring and validation + # + # Note: Additional Azure extensions will be installed in separate configurations + # to provide targeted functionality for specific development scenarios + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Microsoft.VisualStudioCode + directives: + allowPrerelease: true + description: Install Visual Studio Code editor + settings: + id: Microsoft.VisualStudioCode diff --git a/.configuration/devcenter/workloads/ADO/common-frontend-usertasks-config.dsc.yaml b/.configuration/devcenter/workloads/ADO/common-frontend-usertasks-config.dsc.yaml index 9b5851d4..f4e12a7f 100644 --- a/.configuration/devcenter/workloads/ADO/common-frontend-usertasks-config.dsc.yaml +++ b/.configuration/devcenter/workloads/ADO/common-frontend-usertasks-config.dsc.yaml @@ -1,114 +1,114 @@ -# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 -# -# Frontend Development Configuration Sample for .NET Engineers -# ============================================= -# -# Purpose: -# This DSC configuration creates a complete Azure Frontend development environment with: -# - .NET development tools in both environments -# - API testing tools (Postman) -# -# Requirements: -# - with Windows 11 -# - Administrator access for tool installation -# - Internet connectivity for package downloads -# -# Best Practices: -# - Uses Windows and WSL2 for a complete cross-platform experience -# - Applies principle of least privilege where possible -# - Maintains consistent environment between Windows and Linux -# - Uses official Microsoft installation methods for all tools -# -# Maintainer: DevExp Team -# -properties: - configurationVersion: 0.2.0 - - resources: - # Resource: Postman API Platform - # Postman enables API testing and documentation, ensuring reliable integrations - # between Frontend services and Azure APIs. This tool is essential for: - # - Testing REST API endpoints from Azure services - # - Creating and managing API collections for consistent testing - # - Generating API documentation for team collaboration - # - Setting up automated test suites for frontend-to-backend integration - # - # Using WinGet ensures we install the official, verified package. - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Postman.Postman - directives: - description: Install Postman API platform for designing, testing and documenting APIs - allowPrerelease: true - settings: - id: Postman.Postman - - # Resource: Visual Studio Code Extensions - # Installs essential extensions for frontend development with Azure integration. - # These extensions provide syntax highlighting, IntelliSense, debugging, and other - # features specific to Azure and web development technologies. - # - # Following best practices for developer productivity and - # standardized tooling across development teams. - - resource: PSDscResources/Script - id: Microsoft.VisualStudioCode.Extensions - directives: - description: Install VS Code Extensions for Azure frontend development - securityContext: elevated # Requires admin rights to install extensions for all users - allowPrerelease: true # Allow prerelease versions when needed for latest features - settings: - SetScript: | - try { - # Set execution policy to bypass for automation - # Required for running scripts in restricted environments - Set-ExecutionPolicy Bypass -Scope Process -Force - - # Ensure VS Code CLI is in PATH - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") - - Write-Verbose "Installing VS Code extensions for Azure frontend development..." -Verbose - - # PowerShell extension for Azure automation scripts and DevOps - code --install-extension ms-vscode.powershell - - # WSL integration for cross-platform development targeting Linux environments - code --install-extension ms-vscode-remote.remote-wsl - - # C# extension for backend integration and full-stack development - code --install-extension ms-dotnettools.csdevkit - - # TypeScript with latest features for modern web development - code --install-extension ms-vscode.vscode-typescript-next - - # YAML support for deployment configurations and GitHub Actions - code --install-extension redhat.vscode-yaml - - # Bicep for Azure infrastructure as code deployments - # Follows Azure best practice for IaC with native Azure syntax - code --install-extension ms-azuretools.vscode-bicep - - # Azure Tools pack for comprehensive Azure service integration - code --install-extension ms-vscode.vscode-node-azure-pack - - # Azure CLI tools for command-line Azure management - code --install-extension ms-vscode.azurecli - - # GitHub integration for repository management - # Essential for modern DevOps workflows with Azure - code --install-extension GitHub.remotehub - - # GitHub Pull Request integration for code reviews - code --install-extension GitHub.vscode-pull-request-github - - # Postman extension for API testing and documentation - code --install-extension Postman.postman-for-vscode - - Write-Verbose "VS Code extensions installed successfully" -Verbose - } catch { - Write-Error "Failed to install VS Code extensions: $_" - } - # Returns information about installed extensions - # In a fully idempotent implementation, this would check for specific extensions - GetScript: return $false - # Check if VSCode extensions need to be installed - # In a fully idempotent implementation, this would check for specific extensions - TestScript: return $false +# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 +# +# Frontend Development Configuration Sample for .NET Engineers +# ============================================= +# +# Purpose: +# This DSC configuration creates a complete Azure Frontend development environment with: +# - .NET development tools in both environments +# - API testing tools (Postman) +# +# Requirements: +# - with Windows 11 +# - Administrator access for tool installation +# - Internet connectivity for package downloads +# +# Best Practices: +# - Uses Windows and WSL2 for a complete cross-platform experience +# - Applies principle of least privilege where possible +# - Maintains consistent environment between Windows and Linux +# - Uses official Microsoft installation methods for all tools +# +# Maintainer: DevExp Team +# +properties: + configurationVersion: 0.2.0 + + resources: + # Resource: Postman API Platform + # Postman enables API testing and documentation, ensuring reliable integrations + # between Frontend services and Azure APIs. This tool is essential for: + # - Testing REST API endpoints from Azure services + # - Creating and managing API collections for consistent testing + # - Generating API documentation for team collaboration + # - Setting up automated test suites for frontend-to-backend integration + # + # Using WinGet ensures we install the official, verified package. + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Postman.Postman + directives: + description: Install Postman API platform for designing, testing and documenting APIs + allowPrerelease: true + settings: + id: Postman.Postman + + # Resource: Visual Studio Code Extensions + # Installs essential extensions for frontend development with Azure integration. + # These extensions provide syntax highlighting, IntelliSense, debugging, and other + # features specific to Azure and web development technologies. + # + # Following best practices for developer productivity and + # standardized tooling across development teams. + - resource: PSDscResources/Script + id: Microsoft.VisualStudioCode.Extensions + directives: + description: Install VS Code Extensions for Azure frontend development + securityContext: elevated # Requires admin rights to install extensions for all users + allowPrerelease: true # Allow prerelease versions when needed for latest features + settings: + SetScript: | + try { + # Set execution policy to bypass for automation + # Required for running scripts in restricted environments + Set-ExecutionPolicy Bypass -Scope Process -Force + + # Ensure VS Code CLI is in PATH + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + + Write-Verbose "Installing VS Code extensions for Azure frontend development..." -Verbose + + # PowerShell extension for Azure automation scripts and DevOps + code --install-extension ms-vscode.powershell + + # WSL integration for cross-platform development targeting Linux environments + code --install-extension ms-vscode-remote.remote-wsl + + # C# extension for backend integration and full-stack development + code --install-extension ms-dotnettools.csdevkit + + # TypeScript with latest features for modern web development + code --install-extension ms-vscode.vscode-typescript-next + + # YAML support for deployment configurations and GitHub Actions + code --install-extension redhat.vscode-yaml + + # Bicep for Azure infrastructure as code deployments + # Follows Azure best practice for IaC with native Azure syntax + code --install-extension ms-azuretools.vscode-bicep + + # Azure Tools pack for comprehensive Azure service integration + code --install-extension ms-vscode.vscode-node-azure-pack + + # Azure CLI tools for command-line Azure management + code --install-extension ms-vscode.azurecli + + # GitHub integration for repository management + # Essential for modern DevOps workflows with Azure + code --install-extension GitHub.remotehub + + # GitHub Pull Request integration for code reviews + code --install-extension GitHub.vscode-pull-request-github + + # Postman extension for API testing and documentation + code --install-extension Postman.postman-for-vscode + + Write-Verbose "VS Code extensions installed successfully" -Verbose + } catch { + Write-Error "Failed to install VS Code extensions: $_" + } + # Returns information about installed extensions + # In a fully idempotent implementation, this would check for specific extensions + GetScript: return $false + # Check if VSCode extensions need to be installed + # In a fully idempotent implementation, this would check for specific extensions + TestScript: return $false diff --git a/.configuration/devcenter/workloads/winget-update.ps1 b/.configuration/devcenter/workloads/winget-update.ps1 index 8af7b78c..1c4f5db3 100644 --- a/.configuration/devcenter/workloads/winget-update.ps1 +++ b/.configuration/devcenter/workloads/winget-update.ps1 @@ -1,147 +1,147 @@ -Set-ExecutionPolicy Bypass -Scope Process -Force -Clear-Host - -<# -.SYNOPSIS - Quietly updates all Microsoft Store apps using winget (v1.11.x compatible). - -.DESCRIPTION - - Runs fully non-interactive (no prompts). - - Properly orders command + flags for winget 1.11.x. - - Accepts msstore/package agreements on upgrade/install only. - - Uses include-unknown and a forced second pass to catch stubborn Store apps. - - Detects if winget/App Installer updated itself mid-run and retries once. - - Executes winget by absolute path (no App Execution Alias quirks). - - Logs to C:\ProgramData\Winget-StoreUpgrade\upgrade-YYYYMMDD-HHMMSS.log - -.NOTES - Recommended to run in an elevated session to service machine-wide apps. -#> - -# ===== Global non-interactive settings ===== -$ErrorActionPreference = 'Stop' -$ProgressPreference = 'SilentlyContinue' -$env:WINGET_DISABLE_INTERACTIVITY = '1' # environment-based; no CLI side effects - -# ===== Logging ===== -$LogRoot = Join-Path $env:ProgramData 'Winget-StoreUpgrade' -if (-not (Test-Path $LogRoot)) { New-Item -Path $LogRoot -ItemType Directory -Force | Out-Null } -$Timestamp = Get-Date -Format 'yyyyMMdd-HHmmss' -$LogFile = Join-Path $LogRoot "upgrade-$Timestamp.log" - -function Write-Info { param([string]$m) "[INFO ] $m" | Tee-Object -FilePath $LogFile -Append } -function Write-Warn { param([string]$m) "[WARN ] $m" | Tee-Object -FilePath $LogFile -Append } -function Write-Err { param([string]$m) "[ERROR] $m" | Tee-Object -FilePath $LogFile -Append } - -Write-Info "Log file: $LogFile" -Write-Info "Starting Microsoft Store updates..." - -# ===== Robust winget resolution and invoker ===== -function Resolve-WinGetExe { - # Prefer packaged App Installer location (more stable than user alias) - $pkg = Get-AppxPackage -Name Microsoft.DesktopAppInstaller -ErrorAction SilentlyContinue - if ($pkg) { - $candidate = Join-Path $pkg.InstallLocation 'winget.exe' - if (Test-Path $candidate) { return $candidate } - } - # Fallback to whatever PowerShell resolves - return (Get-Command winget.exe -ErrorAction Stop).Source -} - -$script:WinGetExe = Resolve-WinGetExe - -function Invoke-WinGet { - param( - [Parameter(Mandatory)][string[]]$Args, # e.g. @('upgrade','--all',...) - [switch]$RetryOnSelfUpdate # retry once if winget self-updated - ) - # Execute the EXE directly — do NOT call 'winget' then pass a path - $output = & $script:WinGetExe @Args 2>&1 - $text = $output | Out-String - $text | Tee-Object -FilePath $LogFile -Append | Out-Null - - if ($RetryOnSelfUpdate -and $text -match 'Restart the application to complete the upgrade') { - Write-Info "winget/App Installer updated itself; re-resolving path and retrying once..." - Start-Sleep -Milliseconds 500 - $script:WinGetExe = Resolve-WinGetExe - $output = & $script:WinGetExe @Args 2>&1 - $text = $output | Out-String - $text | Tee-Object -FilePath $LogFile -Append | Out-Null - } - return $text -} - -# ===== Preflight: confirm winget exists ===== -try { - Invoke-WinGet -Args @('--version') | Out-Null -} catch { - Write-Err "winget (App Installer) not found. Install/update 'App Installer' from Microsoft Store and re-run." - exit 1 -} - -# ===== Ensure Microsoft Store Install Service is running (helps Store updates) ===== -try { - $svc = Get-Service -Name InstallService -ErrorAction SilentlyContinue - if ($svc -and $svc.Status -ne 'Running') { - Write-Info "Starting Microsoft Store Install Service (InstallService)..." - Start-Service -Name InstallService -ErrorAction SilentlyContinue - } -} catch { Write-Warn "Could not verify/start InstallService. Continuing..." } - -# ===== Ensure msstore source exists and refresh (NO accept flags here) ===== -try { - $sources = Invoke-WinGet -Args @('source','list','--disable-interactivity') - if ($sources -notmatch '(?im)^\s*msstore\b') { - Write-Info "msstore source not found; resetting..." - Invoke-WinGet -Args @('source','reset','--force','msstore','--disable-interactivity') - } - Invoke-WinGet -Args @('source','update','--disable-interactivity') | Out-Null -} catch { - Write-Warn "Winget source operations reported issues; continuing..." -} - -# ===== Optional: prevent winget self-upgrade during the session (pin App Installer) ===== -# (Uncomment if you want to avoid mid-run self-update entirely) -# Invoke-WinGet -Args @('pin','add','--id','Microsoft.AppInstaller','--disable-interactivity') | Out-Null - -# ===== PASS 1: Accept msstore terms & upgrade what winget can detect ===== -Write-Info "Pass 1: upgrading Microsoft Store apps (include-unknown)..." -Invoke-WinGet -Args @( - 'upgrade','--all', - '--source','msstore', - '--include-unknown', - '--silent', - '--accept-source-agreements','--accept-package-agreements', - '--disable-interactivity' -) -RetryOnSelfUpdate | Out-Null - -# ===== PASS 2: Force re-install latest for stragglers where version compare is unknown ===== -Write-Info "Pass 2: forced upgrade (msstore) for remaining/unknown version apps..." -Invoke-WinGet -Args @( - 'upgrade','--all', - '--source','msstore', - '--include-unknown', - '--force', - '--silent', - '--accept-source-agreements','--accept-package-agreements', - '--disable-interactivity' -) -RetryOnSelfUpdate | Out-Null - -# ===== OPTIONAL Safety net pass: catch apps mapped under other sources ===== -Write-Info "Safety net: unfiltered pass to catch any remaining packages..." -Invoke-WinGet -Args @( - 'upgrade','--all', - '--include-unknown', - '--silent', - '--accept-source-agreements','--accept-package-agreements', - '--disable-interactivity' -) -RetryOnSelfUpdate | Out-Null - -# ===== Summary: show any remaining Store upgrades (no accept flags here) ===== -Write-Info "Summary check for remaining Microsoft Store upgrades..." -Invoke-WinGet -Args @('upgrade','--source','msstore','--disable-interactivity') | Out-Null - -# ===== Optional: unpin App Installer after run ===== -# Invoke-WinGet -Args @('pin','remove','--id','Microsoft.AppInstaller','--disable-interactivity') | Out-Null - -Write-Info "Completed. Full log: $LogFile" +Set-ExecutionPolicy Bypass -Scope Process -Force +Clear-Host + +<# +.SYNOPSIS + Quietly updates all Microsoft Store apps using winget (v1.11.x compatible). + +.DESCRIPTION + - Runs fully non-interactive (no prompts). + - Properly orders command + flags for winget 1.11.x. + - Accepts msstore/package agreements on upgrade/install only. + - Uses include-unknown and a forced second pass to catch stubborn Store apps. + - Detects if winget/App Installer updated itself mid-run and retries once. + - Executes winget by absolute path (no App Execution Alias quirks). + - Logs to C:\ProgramData\Winget-StoreUpgrade\upgrade-YYYYMMDD-HHMMSS.log + +.NOTES + Recommended to run in an elevated session to service machine-wide apps. +#> + +# ===== Global non-interactive settings ===== +$ErrorActionPreference = 'Stop' +$ProgressPreference = 'SilentlyContinue' +$env:WINGET_DISABLE_INTERACTIVITY = '1' # environment-based; no CLI side effects + +# ===== Logging ===== +$LogRoot = Join-Path $env:ProgramData 'Winget-StoreUpgrade' +if (-not (Test-Path $LogRoot)) { New-Item -Path $LogRoot -ItemType Directory -Force | Out-Null } +$Timestamp = Get-Date -Format 'yyyyMMdd-HHmmss' +$LogFile = Join-Path $LogRoot "upgrade-$Timestamp.log" + +function Write-Info { param([string]$m) "[INFO ] $m" | Tee-Object -FilePath $LogFile -Append } +function Write-Warn { param([string]$m) "[WARN ] $m" | Tee-Object -FilePath $LogFile -Append } +function Write-Err { param([string]$m) "[ERROR] $m" | Tee-Object -FilePath $LogFile -Append } + +Write-Info "Log file: $LogFile" +Write-Info "Starting Microsoft Store updates..." + +# ===== Robust winget resolution and invoker ===== +function Resolve-WinGetExe { + # Prefer packaged App Installer location (more stable than user alias) + $pkg = Get-AppxPackage -Name Microsoft.DesktopAppInstaller -ErrorAction SilentlyContinue + if ($pkg) { + $candidate = Join-Path $pkg.InstallLocation 'winget.exe' + if (Test-Path $candidate) { return $candidate } + } + # Fallback to whatever PowerShell resolves + return (Get-Command winget.exe -ErrorAction Stop).Source +} + +$script:WinGetExe = Resolve-WinGetExe + +function Invoke-WinGet { + param( + [Parameter(Mandatory)][string[]]$Args, # e.g. @('upgrade','--all',...) + [switch]$RetryOnSelfUpdate # retry once if winget self-updated + ) + # Execute the EXE directly — do NOT call 'winget' then pass a path + $output = & $script:WinGetExe @Args 2>&1 + $text = $output | Out-String + $text | Tee-Object -FilePath $LogFile -Append | Out-Null + + if ($RetryOnSelfUpdate -and $text -match 'Restart the application to complete the upgrade') { + Write-Info "winget/App Installer updated itself; re-resolving path and retrying once..." + Start-Sleep -Milliseconds 500 + $script:WinGetExe = Resolve-WinGetExe + $output = & $script:WinGetExe @Args 2>&1 + $text = $output | Out-String + $text | Tee-Object -FilePath $LogFile -Append | Out-Null + } + return $text +} + +# ===== Preflight: confirm winget exists ===== +try { + Invoke-WinGet -Args @('--version') | Out-Null +} catch { + Write-Err "winget (App Installer) not found. Install/update 'App Installer' from Microsoft Store and re-run." + exit 1 +} + +# ===== Ensure Microsoft Store Install Service is running (helps Store updates) ===== +try { + $svc = Get-Service -Name InstallService -ErrorAction SilentlyContinue + if ($svc -and $svc.Status -ne 'Running') { + Write-Info "Starting Microsoft Store Install Service (InstallService)..." + Start-Service -Name InstallService -ErrorAction SilentlyContinue + } +} catch { Write-Warn "Could not verify/start InstallService. Continuing..." } + +# ===== Ensure msstore source exists and refresh (NO accept flags here) ===== +try { + $sources = Invoke-WinGet -Args @('source','list','--disable-interactivity') + if ($sources -notmatch '(?im)^\s*msstore\b') { + Write-Info "msstore source not found; resetting..." + Invoke-WinGet -Args @('source','reset','--force','msstore','--disable-interactivity') + } + Invoke-WinGet -Args @('source','update','--disable-interactivity') | Out-Null +} catch { + Write-Warn "Winget source operations reported issues; continuing..." +} + +# ===== Optional: prevent winget self-upgrade during the session (pin App Installer) ===== +# (Uncomment if you want to avoid mid-run self-update entirely) +# Invoke-WinGet -Args @('pin','add','--id','Microsoft.AppInstaller','--disable-interactivity') | Out-Null + +# ===== PASS 1: Accept msstore terms & upgrade what winget can detect ===== +Write-Info "Pass 1: upgrading Microsoft Store apps (include-unknown)..." +Invoke-WinGet -Args @( + 'upgrade','--all', + '--source','msstore', + '--include-unknown', + '--silent', + '--accept-source-agreements','--accept-package-agreements', + '--disable-interactivity' +) -RetryOnSelfUpdate | Out-Null + +# ===== PASS 2: Force re-install latest for stragglers where version compare is unknown ===== +Write-Info "Pass 2: forced upgrade (msstore) for remaining/unknown version apps..." +Invoke-WinGet -Args @( + 'upgrade','--all', + '--source','msstore', + '--include-unknown', + '--force', + '--silent', + '--accept-source-agreements','--accept-package-agreements', + '--disable-interactivity' +) -RetryOnSelfUpdate | Out-Null + +# ===== OPTIONAL Safety net pass: catch apps mapped under other sources ===== +Write-Info "Safety net: unfiltered pass to catch any remaining packages..." +Invoke-WinGet -Args @( + 'upgrade','--all', + '--include-unknown', + '--silent', + '--accept-source-agreements','--accept-package-agreements', + '--disable-interactivity' +) -RetryOnSelfUpdate | Out-Null + +# ===== Summary: show any remaining Store upgrades (no accept flags here) ===== +Write-Info "Summary check for remaining Microsoft Store upgrades..." +Invoke-WinGet -Args @('upgrade','--source','msstore','--disable-interactivity') | Out-Null + +# ===== Optional: unpin App Installer after run ===== +# Invoke-WinGet -Args @('pin','remove','--id','Microsoft.AppInstaller','--disable-interactivity') | Out-Null + +Write-Info "Completed. Full log: $LogFile" diff --git a/.configuration/devcenter/workloads/winget-upgrade-packages.dsc.yaml b/.configuration/devcenter/workloads/winget-upgrade-packages.dsc.yaml index 111c9758..35ea99d3 100644 --- a/.configuration/devcenter/workloads/winget-upgrade-packages.dsc.yaml +++ b/.configuration/devcenter/workloads/winget-upgrade-packages.dsc.yaml @@ -1,76 +1,76 @@ -# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 -# -# Common Configuration Sample for .NET Engineers -# ================================= -# -# Purpose: -# This DSC configuration sets up a standard development environment with: -# - Development storage using Dev Drive -# - Source control tools (Git, GitHub CLI) -# - Development runtimes (.NET 9 SDK and Runtime) -# - Development tools (VS Code, Node.js) -# -# Prerequisites: -# - Windows 10/11 with admin privileges -# - Internet connectivity for package downloads -# -# Maintainers: DevExp Team - -properties: - configurationVersion: "0.2.0" - resources: - - resource: PSDscResources/Script - id: Winget-Upgrade-Packages - directives: - description: Upgrade all Microsoft Store apps using winget - securityContext: elevated # Requires admin rights to install apps - settings: - SetScript: | - $ErrorActionPreference = 'Stop' - $ProgressPreference = 'SilentlyContinue' - Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force | Out-Null - - $scriptUrl = 'https://raw.githubusercontent.com/Evilazaro/DevExp-DevBox/main/.configuration/devcenter/workloads/winget-update.ps1' - $downloadDir = Join-Path $env:TEMP 'DevExp-DevBox-Setup' - $localScript = Join-Path $downloadDir 'winget-update.ps1' - - # Ensure TLS 1.2 for older hosts - try { - [System.Net.ServicePointManager]::SecurityProtocol = ` - [System.Net.ServicePointManager]::SecurityProtocol -bor [System.Net.SecurityProtocolType]::Tls12 - } catch { } - - # Create temp folder if needed - if (-not (Test-Path -LiteralPath $downloadDir)) { - New-Item -ItemType Directory -Path $downloadDir -Force | Out-Null - } - - # Download script quietly (retry up to 3 times) - $maxAttempts = 3 - for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { - try { - Invoke-WebRequest -Uri $scriptUrl -OutFile $localScript -UseBasicParsing -TimeoutSec 60 -ErrorAction Stop - break - } catch { - if ($attempt -eq $maxAttempts) { - Write-Error "Failed to download script after $maxAttempts attempts. $_" - exit 1 - } - Start-Sleep -Seconds ([int][Math]::Pow(2, $attempt)) - } - } - - # Validate and unblock - if (-not (Test-Path -LiteralPath $localScript) -or (Get-Item -LiteralPath $localScript).Length -le 0) { - Write-Error "Downloaded script is missing or empty: $localScript" - exit 1 - } - try { Unblock-File -LiteralPath $localScript } catch { } - - # Run the script in a separate quiet PowerShell process - Start-Process powershell.exe -ArgumentList @( - '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $localScript - ) -WindowStyle Hidden -Wait - - GetScript: return $false +# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 +# +# Common Configuration Sample for .NET Engineers +# ================================= +# +# Purpose: +# This DSC configuration sets up a standard development environment with: +# - Development storage using Dev Drive +# - Source control tools (Git, GitHub CLI) +# - Development runtimes (.NET 9 SDK and Runtime) +# - Development tools (VS Code, Node.js) +# +# Prerequisites: +# - Windows 10/11 with admin privileges +# - Internet connectivity for package downloads +# +# Maintainers: DevExp Team + +properties: + configurationVersion: "0.2.0" + resources: + - resource: PSDscResources/Script + id: Winget-Upgrade-Packages + directives: + description: Upgrade all Microsoft Store apps using winget + securityContext: elevated # Requires admin rights to install apps + settings: + SetScript: | + $ErrorActionPreference = 'Stop' + $ProgressPreference = 'SilentlyContinue' + Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force | Out-Null + + $scriptUrl = 'https://raw.githubusercontent.com/Evilazaro/DevExp-DevBox/main/.configuration/devcenter/workloads/winget-update.ps1' + $downloadDir = Join-Path $env:TEMP 'DevExp-DevBox-Setup' + $localScript = Join-Path $downloadDir 'winget-update.ps1' + + # Ensure TLS 1.2 for older hosts + try { + [System.Net.ServicePointManager]::SecurityProtocol = ` + [System.Net.ServicePointManager]::SecurityProtocol -bor [System.Net.SecurityProtocolType]::Tls12 + } catch { } + + # Create temp folder if needed + if (-not (Test-Path -LiteralPath $downloadDir)) { + New-Item -ItemType Directory -Path $downloadDir -Force | Out-Null + } + + # Download script quietly (retry up to 3 times) + $maxAttempts = 3 + for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { + try { + Invoke-WebRequest -Uri $scriptUrl -OutFile $localScript -UseBasicParsing -TimeoutSec 60 -ErrorAction Stop + break + } catch { + if ($attempt -eq $maxAttempts) { + Write-Error "Failed to download script after $maxAttempts attempts. $_" + exit 1 + } + Start-Sleep -Seconds ([int][Math]::Pow(2, $attempt)) + } + } + + # Validate and unblock + if (-not (Test-Path -LiteralPath $localScript) -or (Get-Item -LiteralPath $localScript).Length -le 0) { + Write-Error "Downloaded script is missing or empty: $localScript" + exit 1 + } + try { Unblock-File -LiteralPath $localScript } catch { } + + # Run the script in a separate quiet PowerShell process + Start-Process powershell.exe -ArgumentList @( + '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $localScript + ) -WindowStyle Hidden -Wait + + GetScript: return $false TestScript: return $false \ No newline at end of file diff --git a/infra/settings/workload/devcenter.schema.json b/infra/settings/workload/devcenter.schema.json index 3861bffb..c425d883 100644 --- a/infra/settings/workload/devcenter.schema.json +++ b/infra/settings/workload/devcenter.schema.json @@ -1,596 +1,433 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://schemas.devexp.io/devcenter-settings.json", - "title": "DevCenter Configuration", - "description": "Schema for defining Microsoft DevCenter settings including projects, pools, and environment configurations.", - "type": "object", - "additionalProperties": false, - "required": [ - "name", - "projects", - "tags" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the DevCenter resource", - "minLength": 1, - "maxLength": 64, - "pattern": "^[a-zA-Z0-9][a-zA-Z0-9\\-_.]*[a-zA-Z0-9]$", - "examples": [ - "contoso-devcenter", - "my-devcenter" - ] - }, - "catalogItemSyncEnableStatus": { - "type": "string", - "description": "Status of catalog item synchronization", - "enum": [ - "Enabled", - "Disabled" - ], - "default": "Enabled" - }, - "microsoftHostedNetworkEnableStatus": { - "type": "string", - "description": "Status of Microsoft hosted network feature", - "enum": [ - "Enabled", - "Disabled" - ], - "default": "Enabled" - }, - "installAzureMonitorAgentEnableStatus": { - "type": "string", - "description": "Status of Azure Monitor agent installation", - "enum": [ - "Enabled", - "Disabled" - ], - "default": "Enabled" - }, - "identity": { - "type": "object", - "description": "Identity configuration for the DevCenter resource", - "additionalProperties": false, - "required": [ - "type" - ], - "properties": { - "type": { - "type": "string", - "description": "Type of managed identity", - "enum": [ - "SystemAssigned", - "UserAssigned" - ], - "default": "SystemAssigned" - }, - "roleAssignments": { - "type": "object", - "description": "Role assignments for the DevCenter identity", - "additionalProperties": false, - "properties": { - "devCenter": { - "type": "array", - "description": "Role assignments for DevCenter operations", - "items": { - "$ref": "#/definitions/roleAssignment" - } - }, - "orgRoleTypes": { - "type": "array", - "description": "Organizational role type configurations", - "items": { - "$ref": "#/definitions/orgRoleType" - } - } - } - } - } - }, - "catalogs": { - "type": "array", - "description": "Global catalogs available to all projects", - "items": { - "$ref": "#/definitions/catalog" - } - }, - "environmentTypes": { - "type": "array", - "description": "Global environment types available to all projects", - "items": { - "$ref": "#/definitions/environmentType" - } - }, - "projects": { - "type": "array", - "description": "DevCenter projects configuration", - "minItems": 1, - "items": { - "$ref": "#/definitions/project" - } - }, - "tags": { - "type": "object", - "description": "Resource tags for the DevCenter", - "additionalProperties": { - "type": "string", - "minLength": 1, - "maxLength": 256 - }, - "maxProperties": 50, - "examples": [ - { - "environment": "dev", - "division": "Platforms", - "team": "DevExP", - "project": "DevExP-DevBox", - "costCenter": "IT", - "owner": "Contoso", - "resources": "DevCenter" - } - ] - } - }, - "definitions": { - "roleAssignment": { - "type": "object", - "description": "Azure RBAC role assignment", - "additionalProperties": false, - "required": [ - "id", - "name", - "scope" - ], - "properties": { - "id": { - "type": "string", - "description": "UUID of the role definition", - "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", - "examples": [ - "b24988ac-6180-42a0-ab88-20f7382dd24c" - ] - }, + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Dev Center configuration schema", + "type": "object", + "additionalProperties": false, + "properties": { "name": { - "type": "string", - "description": "Name of the role", - "minLength": 1, - "examples": [ - "Contributor", - "Reader" - ] - }, - "scope": { - "type": "string", - "description": "Scope of the role assignment", - "enum": [ - "Subscription", - "ResourceGroup", - "Project" - ], - "default": "Subscription", - "examples": [ - "Subscription", - "ResourceGroup", - "Project" - ] - } - } - }, - "orgRoleType": { - "type": "object", - "description": "Organizational role type configuration", - "additionalProperties": false, - "required": [ - "type", - "azureADGroupId", - "azureADGroupName", - "azureRBACRoles" - ], - "properties": { - "type": { - "type": "string", - "description": "Type of organizational role", - "examples": [ - "DevManager", - "Developer" - ] - }, - "azureADGroupId": { - "type": "string", - "description": "Azure AD group ID", - "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" - }, - "azureADGroupName": { - "type": "string", - "description": "Azure AD group name", - "minLength": 1 - }, - "azureRBACRoles": { - "type": "array", - "description": "Azure RBAC roles assigned to this group", - "items": { - "$ref": "#/definitions/roleAssignment" - } - } - } - }, - "catalog": { - "type": "object", - "description": "Catalog configuration", - "additionalProperties": false, - "required": [ - "name", - "type", - "uri", - "branch", - "path" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the catalog", - "minLength": 1 - }, - "type": { - "type": "string", - "description": "Type of catalog repository", - "enum": [ - "gitHub", - "adoGit" - ], - "examples": [ - "gitHub" - ] - }, - "uri": { - "type": "string", - "description": "URI of the catalog repository", - "format": "uri" - }, - "branch": { - "type": "string", - "description": "Branch name in the repository", - "default": "main" - }, - "path": { - "type": "string", - "description": "Path within the repository", - "examples": [ - ".configuration/devcenter/tasks" - ] - } - } - }, - "environmentType": { - "type": "object", - "description": "Environment type configuration", - "additionalProperties": false, - "required": [ - "name", - "deploymentTargetId" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the environment type", - "minLength": 1, - "examples": [ - "dev", - "staging", - "prod" - ] + "type": "string", + "minLength": 1 }, - "deploymentTargetId": { - "type": "string", - "description": "Deployment target identifier (empty for default subscription)", - "examples": [ - "", - "subscription-id" - ] - } - } - }, - "project": { - "type": "object", - "description": "DevCenter project configuration", - "additionalProperties": false, - "required": [ - "name" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the project", - "minLength": 1, - "maxLength": 64, - "pattern": "^[a-zA-Z0-9][a-zA-Z0-9\\-_.]*[a-zA-Z0-9]$" + "catalogItemSyncEnableStatus": { + "type": "string", + "enum": [ + "Enabled", + "Disabled" + ] }, - "description": { - "type": "string", - "description": "Description of the project", - "maxLength": 512 + "microsoftHostedNetworkEnableStatus": { + "type": "string", + "enum": [ + "Enabled", + "Disabled" + ] }, - "network": { - "type": "object", - "description": "Network configuration for the project", - "properties": { - "name": { - "type": "string", - "description": "Name of the network configuration", - "minLength": 1 - }, - "create": { - "type": "boolean", - "description": "Whether to create the network resources", - "default": true - }, - "resourceGroupName": { - "type": "string", - "description": "Name of the resource group for network resources", - "minLength": 1 - }, - "virtualNetworkType": { - "type": "string", - "description": "Type of virtual network", - "enum": [ - "Managed", - "Unmanaged" - ], - "default": "Managed" - }, - "addressPrefixes": { - "type": "array", - "description": "Address prefixes for the virtual network", - "items": { - "type": "string", - "format": "cidr" - } - }, - "subnets": { - "type": "array", - "description": "Subnets within the virtual network", - "items": { - "$ref": "#/definitions/subnet" - } - } - } + "installAzureMonitorAgentEnableStatus": { + "type": "string", + "enum": [ + "Enabled", + "Disabled" + ] }, "identity": { - "type": "object", - "description": "Identity configuration for the project", - "additionalProperties": false, - "required": [ - "type", - "roleAssignments" - ], - "properties": { - "type": { - "type": "string", - "description": "Type of managed identity", - "enum": [ - "SystemAssigned", - "UserAssigned" - ], - "default": "SystemAssigned" + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "SystemAssigned", + "UserAssigned", + "SystemAssignedUserAssigned", + "None" + ] + }, + "roleAssignments": { + "type": "object", + "additionalProperties": false, + "properties": { + "devCenter": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignment" + } + }, + "orgRoleTypes": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string" + }, + "azureADGroupId": { + "$ref": "#/definitions/guid" + }, + "azureADGroupName": { + "type": "string" + }, + "azureRBACRoles": { + "type": "array", + "items": { + "$ref": "#/definitions/rbacRole" + } + } + }, + "required": [ + "type", + "azureADGroupId", + "azureADGroupName" + ] + } + } + } + } }, - "roleAssignments": { - "type": "array", - "description": "Role assignments for project users", - "items": { - "$ref": "#/definitions/projectRoleAssignment" - } - } - } - }, - "pools": { - "type": "array", - "description": "DevBox pools for the project", - "items": { - "$ref": "#/definitions/pool" - } - }, - "environmentTypes": { - "type": "array", - "description": "Environment types available to the project", - "items": { - "$ref": "#/definitions/environmentType" - } + "required": [ + "type" + ] }, "catalogs": { - "type": "object", - "description": "Project-specific catalogs", - "additionalProperties": false, - "properties": { - "environmentDefinition": { - "$ref": "#/definitions/catalog" - }, - "imageDefinition": { - "$ref": "#/definitions/catalog" + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string" + }, + "visibility": { + "type": "string", + "enum": [ + "public", + "private" + ] + }, + "uri": { + "type": "string", + "format": "uri" + }, + "branch": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sourceControl": { + "type": "string" + } + }, + "required": [ + "name", + "type", + "uri" + ] } - } - }, - "tags": { - "type": "object", - "description": "Resource tags for the project", - "additionalProperties": { - "type": "string", - "minLength": 1, - "maxLength": 256 - }, - "maxProperties": 50 - } - } - }, - "networkConfiguration": { - "type": "object", - "description": "Network configuration for a project", - "additionalProperties": false, - "required": [ - "name", - "create", - "resourceGroupName", - "virtualNetworkType", - "addressPrefixes", - "subnets" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the network configuration", - "minLength": 1 - }, - "create": { - "type": "boolean", - "description": "Whether to create the network resources", - "default": true - }, - "resourceGroupName": { - "type": "string", - "description": "Name of the resource group for network resources", - "minLength": 1 }, - "virtualNetworkType": { - "type": "string", - "description": "Type of virtual network", - "enum": [ - "Managed", - "Unmanaged" - ], - "default": "Managed" - }, - "addressPrefixes": { - "type": "array", - "description": "Address prefixes for the virtual network", - "minItems": 1, - "items": { - "type": "string", - "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\/(?:[0-9]|[1-2][0-9]|3[0-2])$" - } + "environmentTypes": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "deploymentTargetId": { + "type": "string" + } + }, + "required": [ + "name" + ] + } }, - "subnets": { - "type": "array", - "description": "Subnets within the virtual network", - "minItems": 1, - "items": { - "$ref": "#/definitions/subnet" - } + "projects": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "network": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "create": { + "type": "boolean" + }, + "resourceGroupName": { + "type": "string" + }, + "virtualNetworkType": { + "type": "string" + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string", + "pattern": "^(?:\\d{1,3}\\.){3}\\d{1,3}\\/\\d{1,2}$" + } + }, + "subnets": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "properties": { + "type": "object", + "additionalProperties": false, + "properties": { + "addressPrefix": { + "type": "string", + "pattern": "^(?:\\d{1,3}\\.){3}\\d{1,3}\\/\\d{1,2}$" + } + }, + "required": [ + "addressPrefix" + ] + } + }, + "required": [ + "name", + "properties" + ] + } + }, + "tags": { + "type": "object", + "additionalProperties": false, + "properties": { + "environment": { + "type": "string" + }, + "division": { + "type": "string" + }, + "team": { + "type": "string" + }, + "project": { + "type": "string" + }, + "costCenter": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "resources": { + "type": "string" + } + } + } + }, + "required": [ + "name", + "create" + ] + }, + "identity": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string" + }, + "roleAssignments": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "azureADGroupId": { + "$ref": "#/definitions/guid" + }, + "azureADGroupName": { + "type": "string" + }, + "azureRBACRoles": { + "type": "array", + "items": { + "$ref": "#/definitions/rbacRole" + } + } + }, + "required": [ + "azureADGroupId", + "azureADGroupName" + ] + } + } + }, + "required": [ + "type" + ] + }, + "pools": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "imageDefinitionName": { + "type": "string" + }, + "vmSku": { + "type": "string" + } + }, + "required": [ + "name", + "imageDefinitionName" + ] + } + }, + "environmentTypes": { + "type": "array", + "items": { + "$ref": "#/properties/environmentTypes/items" + } + }, + "catalogs": { + "type": "array", + "items": { + "$ref": "#/properties/catalogs/items" + } + }, + "tags": { + "type": "object", + "additionalProperties": false, + "properties": { + "environment": { + "type": "string" + }, + "division": { + "type": "string" + }, + "team": { + "type": "string" + }, + "project": { + "type": "string" + }, + "costCenter": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "resources": { + "type": "string" + } + } + } + }, + "required": [ + "name" + ] + } }, "tags": { - "type": "object", - "description": "Tags for the network resources", - "additionalProperties": { - "type": "string", - "minLength": 1, - "maxLength": 256 - } - } - } - }, - "subnet": { - "type": "object", - "description": "Subnet configuration", - "additionalProperties": false, - "required": [ - "name", - "properties" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the subnet", - "minLength": 1 - }, - "properties": { - "type": "object", - "description": "Subnet properties", - "additionalProperties": false, - "required": [ - "addressPrefix" - ], - "properties": { - "addressPrefix": { - "type": "string", - "description": "Address prefix for the subnet", - "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\/(?:[0-9]|[1-2][0-9]|3[0-2])$" + "type": "object", + "additionalProperties": false, + "properties": { + "environment": { + "type": "string" + }, + "division": { + "type": "string" + }, + "team": { + "type": "string" + }, + "project": { + "type": "string" + }, + "costCenter": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "resources": { + "type": "string" + } } - } } - } }, - "projectRoleAssignment": { - "type": "object", - "description": "Role assignment for project users", - "additionalProperties": false, - "required": [ - "azureADGroupId", - "azureADGroupName", - "azureRBACRoles" - ], - "properties": { - "azureADGroupId": { - "type": "string", - "description": "Azure AD group ID", - "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" - }, - "azureADGroupName": { - "type": "string", - "description": "Azure AD group name", - "minLength": 1 - }, - "azureRBACRoles": { - "type": "array", - "description": "Azure RBAC roles for the group", - "items": { - "$ref": "#/definitions/roleAssignment" - } - } - } - }, - "pool": { - "type": "object", - "description": "DevBox pool configuration", - "additionalProperties": false, - "required": [ - "name", - "imageDefinitionName", - "vmSku" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the DevBox pool", - "minLength": 1, - "examples": [ - "backend-engineer", - "frontend-engineer" - ] + "required": [ + "name" + ], + "definitions": { + "guid": { + "type": "string", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" }, - "imageDefinitionName": { - "type": "string", - "description": "Name of the image definition to use", - "minLength": 1 + "roleAssignment": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "$ref": "#/definitions/guid" + }, + "name": { + "type": "string" + }, + "scope": { + "type": "string", + "enum": [ + "Subscription", + "ResourceGroup", + "Project", + "Tenant", + "ManagementGroup" + ] + } + }, + "required": [ + "id", + "name", + "scope" + ] }, - "vmSku": { - "type": "string", - "description": "SKU of the virtual machine to use for this pool", - "minLength": 1, - "enum": [ - "general_i_8c32gb256ssd_v2", - "general_i_8c32gb512ssd_v2", - "general_i_8c32gb1024ssd_v2", - "general_i_8c32gb2048ssd_v2", - "general_i_16c64gb256ssd_v2", - "general_i_16c64gb512ssd_v2", - "general_i_16c64gb1024ssd_v2", - "general_i_16c64gb2048ssd_v2", - "general_i_32c128gb512ssd_v2", - "general_i_32c128gb1024ssd_v2", - "general_i_32c128gb2048ssd_v2" - ] + "rbacRole": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "id": { + "$ref": "#/definitions/guid" + }, + "scope": { + "type": "string" + } + }, + "required": [ + "name", + "id" + ] } - } } - } } \ No newline at end of file diff --git a/infra/settings/workload/devcenter.yaml b/infra/settings/workload/devcenter.yaml index 83851154..10f27b63 100644 --- a/infra/settings/workload/devcenter.yaml +++ b/infra/settings/workload/devcenter.yaml @@ -62,9 +62,10 @@ identity: catalogs: - name: "customTasks" type: gitHub - uri: "https://github.com/Evilazaro/DevExp-DevBox.git" + visibility: public + uri: "https://github.com/microsoft/devcenter-catalog.git" branch: "main" - path: "/.configuration/devcenter/tasks" + path: "./Tasks" # Environment Types section - defines deployment environments for applications # Each environment type represents a different stage in the development lifecycle @@ -81,103 +82,6 @@ environmentTypes: # Each project has its own Dev Box configurations, catalogs, and permissions # Best practice: Create separate projects for different teams or workstreams projects: - # Identity Provider project - for authentication/authorization services - - name: "identityProvider" # Name of the project - description: "Identity Provider project." # Project description - - network: - name: identityProvider # Name of the virtual network - create: true # Set this parameter to true to create the network. If its value is false, the the accelerator will connect to an existing network. - resourceGroupName: "identityProvider-connectivity-RG" # Resource group for network. If the create parameter is set to false, the resource group must already exist. - virtualNetworkType: Managed # Managed network type is recommended for Dev Center projects to ensure proper connectivity and security - addressPrefixes: # Address space for the virtual network. This should be a unique CIDR block that does not overlap with other networks in your Azure subscription. If the create parameter is set to true, the accelerator will create the network with this address space. - - 10.0.0.0/16 - subnets: - - name: identityProvider-subnet # Subnet name. If the create parameter is set to true, the accelerator will create the subnet with this name and address prefix. If its value is false, the subnet must already exist. - properties: - addressPrefix: 10.0.1.0/24 # Subnet address range. If the create parameter is set to true, the accelerator will create the subnet with this address range. If its value is false, the subnet must already exist. - tags: - environment: dev - division: Platforms - team: DevExP - project: DevExP-DevBox - costCenter: IT - owner: Contoso - resources: Network - - # Project identity configuration - controls project-level security - identity: - type: SystemAssigned - # Role assignments for the Project - # These roles control who can manage the project and its resources - # You must create the corresponding Azure AD groups and assign users to them - # The default values in this example are "Identity Provider Engineers" - # You can customize these values based on your organization's requirements - # The following roles follow the principle of least privilege and best practices described in https://learn.microsoft.com/en-us/azure/dev-box/concept-dev-box-deployment-guide#organizational-roles-and-responsibilities guidance. - roleAssignments: - - azureADGroupId: "67a29bc3-f25c-4599-9cb1-4da19507e8ee" # Azure AD group ID for Identity Provider Engineers. You must create this group in Azure AD and replace the , the default value in this example is "Identity Provider Engineers" - azureADGroupName: "Identity Provider Engineers" # Azure AD group name - azureRBACRoles: - - name: "Contributor" - id: "b24988ac-6180-42a0-ab88-20f7382dd24c" - scope: Project - - name: "Dev Box User" - id: "45d50f46-0b78-4001-a660-4198cbe8cd05" - scope: Project - - name: "Deployment Environment User" - id: "18e40d4e-8d2e-438d-97e1-9528336e149c" - scope: Project - - name: "Key Vault Secrets User" - id: "4633458b-17de-408a-b874-0445c86b69e6" - scope: ResourceGroup - - id: "b86a8fe4-44ce-4948-aee5-eccb2c155cd7" - name: "Key Vault Secrets Officer" - scope: ResourceGroup - - # Dev Box pools - collections of Dev Boxes with specific configurations - # Best practice: Create role-specific pools with appropriate tools and settings - pools: - - name: "backend-engineer" - imageDefinitionName: "identityProvider-backend-engineer" - vmSku: general_i_32c128gb512ssd_v2 - - name: "frontend-engineer" - imageDefinitionName: "identityProvider-frontend-engineer" - vmSku: general_i_16c64gb256ssd_v2 - - # Project-specific environment types - # Defines which deployment environments are available to the project - environmentTypes: - - name: "dev" - deploymentTargetId: "" - - name: "staging" - deploymentTargetId: "" - - # Project-specific catalogs - repositories containing project configurations - catalogs: - environmentDefinition: - name: "environments" - type: gitHub - uri: "https://github.com/Evilazaro/IdentityProvider.git" - branch: "main" - path: "/.configuration/devcenter/environments" - imageDefinition: - name: "imageDefinitions" - type: gitHub - uri: "https://github.com/Evilazaro/IdentityProvider.git" - branch: "main" - path: "/.configuration/devcenter/imageDefinitions" - - # Project-specific tags for resource governance and organization - # Best practice: Apply consistent tags for cost allocation and ownership - tags: - environment: "dev" # Identifies the deployment environment - division: "Platforms" # Organizational division responsible for the project - team: "DevExP" # Team responsible for implementation - project: "DevExP-DevBox" # Project name for cost allocation - costCenter: "IT" # Financial tracking designation - owner: "Contoso" # Resource ownership - resources: "Project" # Resource type identifier - - name: "eShop" description: "eShop project." @@ -247,15 +151,17 @@ projects: # Project-specific catalogs - repositories containing project configurations catalogs: - environmentDefinition: - name: "environments" - type: gitHub + - name: "environments" + type: environmentDefinition + sourceControl: gitHub + visibility: private uri: "https://github.com/Evilazaro/eShop.git" branch: "main" path: "/.devcenter/environments" - imageDefinition: - name: "imageDefinitions" - type: gitHub + - name: "devboxImages" + type: imageDefinition + sourceControl: gitHub + visibility: private uri: "https://github.com/Evilazaro/eShop.git" branch: "main" path: "/.devcenter/imageDefinitions" diff --git a/src/workload/core/catalog.bicep b/src/workload/core/catalog.bicep index abb3f193..34f4fbbb 100644 --- a/src/workload/core/catalog.bicep +++ b/src/workload/core/catalog.bicep @@ -16,6 +16,9 @@ type Catalog = { @description('Type of repository (GitHub or Azure DevOps Git)') type: CatalogType + @description('Visibility of the catalog') + visibility: 'public' | 'private' + @description('URI of the repository') uri: string @@ -48,7 +51,7 @@ resource catalog 'Microsoft.DevCenter/devcenters/catalogs@2025-04-01-preview' = uri: catalogConfig.uri branch: catalogConfig.branch path: catalogConfig.path - secretIdentifier: secretIdentifier + secretIdentifier: (catalogConfig.visibility == 'private') ? secretIdentifier : null } } : { @@ -56,7 +59,7 @@ resource catalog 'Microsoft.DevCenter/devcenters/catalogs@2025-04-01-preview' = uri: catalogConfig.uri branch: catalogConfig.branch path: catalogConfig.path - secretIdentifier: secretIdentifier + secretIdentifier: (catalogConfig.visibility == 'private') ? secretIdentifier : null } } ) diff --git a/src/workload/project/project.bicep b/src/workload/project/project.bicep index 92e00859..cc71bb22 100644 --- a/src/workload/project/project.bicep +++ b/src/workload/project/project.bicep @@ -12,7 +12,7 @@ param logAnalyticsId string param projectDescription string @description('Catalog configuration for the project') -param projectCatalogs object +param catalogs object[] @description('Environment types to be associated with the project') param projectEnvironmentTypes array @@ -139,21 +139,23 @@ module projectADGroup '../../identity/projectIdentityRoleAssignment.bicep' = [ } ] -@description('Configure environment definition catalogs') -module catalogs 'projectCatalog.bicep' = { - name: 'catalog-${uniqueString(project.id)}-${dateTime}' - scope: resourceGroup() - params: { - projectName: project.name - catalogConfig: projectCatalogs - secretIdentifier: secretIdentifier +@description('Configure project catalogs') +module projectCatalogs 'projectCatalog.bicep' = [ + for (catalog, i) in catalogs: { + name: 'catalog-${i}-${uniqueString(project.id, catalog.name)}-${dateTime}' + scope: resourceGroup() + params: { + projectName: project.name + catalogConfig: catalog + secretIdentifier: secretIdentifier + } + dependsOn: [ + projectIdentity + projectIdentityRG + projectADGroup + ] } - dependsOn: [ - projectIdentity - projectIdentityRG - projectADGroup - ] -} +] @description('Configure project environment types') module environmentTypes 'projectEnvironmentType.bicep' = [ @@ -168,7 +170,7 @@ module environmentTypes 'projectEnvironmentType.bicep' = [ projectIdentity projectIdentityRG projectADGroup - catalogs + projectCatalogs ] } ] @@ -187,7 +189,7 @@ module connectivity '../../connectivity/connectivity.bicep' = { projectIdentity projectIdentityRG projectADGroup - catalogs + projectCatalogs ] } @@ -199,7 +201,7 @@ module pools 'projectPool.bicep' = [ params: { name: pool.name projectName: project.name - catalogName: projectCatalogs.imageDefinition.name + catalogs: catalogs imageDefinitionName: pool.imageDefinitionName vmSku: pool.vmSku networkConnectionName: connectivity.outputs.networkConnectionName diff --git a/src/workload/project/projectCatalog.bicep b/src/workload/project/projectCatalog.bicep index 21cd35a6..42f327ac 100644 --- a/src/workload/project/projectCatalog.bicep +++ b/src/workload/project/projectCatalog.bicep @@ -2,7 +2,7 @@ param projectName string @description('Catalog configurations for the project') -param catalogConfig ProjectCatalog +param catalogConfig Catalog @description('Secret identifier for Git repository authentication') @secure() @@ -13,8 +13,14 @@ type Catalog = { @description('Name of the catalog') name: string - @description('Type of repository (GitHub or Azure DevOps Git)') - type: CatalogType + @description('Type of catalog (environment or image)') + type: 'environmentDefinition' | 'imageDefinition' + + @description('Source control type') + sourceControl: 'gitHub' | 'adoGit' + + @description('Visibility of the catalog') + visibility: 'public' | 'private' @description('URI of the repository') uri: string @@ -26,81 +32,32 @@ type Catalog = { path: string } -@description('Project catalog configuration') -type ProjectCatalog = { - @description('Environment definition catalog configuration') - environmentDefinition: Catalog - - @description('Image definition catalog configuration') - imageDefinition: Catalog -} - -@description('Supported catalog repository types') -type CatalogType = string - @description('Reference to the existing DevCenter project') resource project 'Microsoft.DevCenter/projects@2025-04-01-preview' existing = { name: projectName } @description('Environment Definition Catalog') -resource environmentDefinitionCatalog 'Microsoft.DevCenter/projects/catalogs@2025-04-01-preview' = { - name: catalogConfig.environmentDefinition.name - parent: project - properties: { - syncType: 'Scheduled' - gitHub: catalogConfig.environmentDefinition.type == 'gitHub' - ? { - uri: catalogConfig.environmentDefinition.uri - branch: catalogConfig.environmentDefinition.branch - path: catalogConfig.environmentDefinition.path - secretIdentifier: secretIdentifier - } - : null - adoGit: catalogConfig.environmentDefinition.type == 'adoGit' - ? { - uri: catalogConfig.environmentDefinition.uri - branch: catalogConfig.environmentDefinition.branch - path: catalogConfig.environmentDefinition.path - secretIdentifier: secretIdentifier - } - : null - } -} - -@description('Image Definition Catalog') -resource imageDefinitionCatalog 'Microsoft.DevCenter/projects/catalogs@2025-04-01-preview' = { - name: catalogConfig.imageDefinition.name +resource catalog 'Microsoft.DevCenter/projects/catalogs@2025-04-01-preview' = { + name: catalogConfig.name parent: project properties: { syncType: 'Scheduled' - gitHub: catalogConfig.imageDefinition.type == 'gitHub' + gitHub: catalogConfig.sourceControl == 'gitHub' ? { - uri: catalogConfig.imageDefinition.uri - branch: catalogConfig.imageDefinition.branch - path: catalogConfig.imageDefinition.path - secretIdentifier: secretIdentifier + uri: catalogConfig.uri + branch: catalogConfig.branch + path: catalogConfig.path + secretIdentifier: (catalogConfig.visibility == 'private') ? secretIdentifier : null } : null - adoGit: catalogConfig.imageDefinition.type == 'adoGit' + adoGit: catalogConfig.sourceControl == 'adoGit' ? { - uri: catalogConfig.imageDefinition.uri - branch: catalogConfig.imageDefinition.branch - path: catalogConfig.imageDefinition.path - secretIdentifier: secretIdentifier + uri: catalogConfig.uri + branch: catalogConfig.branch + path: catalogConfig.path + secretIdentifier: (catalogConfig.visibility == 'private') ? secretIdentifier : null } : null } } - -@description('The name of the environment definition catalog') -output environmentDefinitionCatalogName string = environmentDefinitionCatalog.name - -@description('The ID of the environment definition catalog') -output environmentDefinitionCatalogId string = environmentDefinitionCatalog.id - -@description('The name of the image definition catalog') -output imageDefinitionCatalogName string = imageDefinitionCatalog.name - -@description('The ID of the image definition catalog') -output imageDefinitionCatalogId string = imageDefinitionCatalog.id diff --git a/src/workload/project/projectPool.bicep b/src/workload/project/projectPool.bicep index 923e7147..3f6b3834 100644 --- a/src/workload/project/projectPool.bicep +++ b/src/workload/project/projectPool.bicep @@ -5,7 +5,7 @@ param name string param location string = resourceGroup().location @description('The name of the catalog to use for the pool') -param catalogName string +param catalogs Catalog[] @description('The name of the dev box definition to use for the pool') param imageDefinitionName string @@ -22,36 +22,59 @@ param networkType string @description('The name of the project to which the pool belongs') param projectName string +@description('Catalog definition') +type Catalog = { + @description('Name of the catalog') + name: string + + @description('Type of catalog (environment or image)') + type: 'environmentDefinition' | 'imageDefinition' + + @description('Source control type') + sourceControl: 'gitHub' | 'adoGit' + + @description('Visibility of the catalog') + visibility: 'public' | 'private' + + @description('URI of the repository') + uri: string + + @description('Branch to sync from') + branch: string + + @description('Path within the repository to sync') + path: string +} + @description('Project') resource project 'Microsoft.DevCenter/projects@2025-04-01-preview' existing = { name: projectName } @description('Dev Box Pool resource') -resource pool 'Microsoft.DevCenter/projects/pools@2025-04-01-preview' = { - name: name - location: location - parent: project - properties: { - devBoxDefinitionType: 'Value' - devBoxDefinitionName: '~Catalog~${catalogName}~${imageDefinitionName}' - devBoxDefinition: { - imageReference: { - id: '${project.id}/images/~Catalog~${catalogName}~${imageDefinitionName}' - } - sku: { - name: vmSku +resource pool 'Microsoft.DevCenter/projects/pools@2025-04-01-preview' = [ + for (catalog, i) in catalogs: if (catalog.type == 'imageDefinition') { + name: '${name}-${i}-pool' + location: location + parent: project + properties: { + devBoxDefinitionType: 'Value' + devBoxDefinitionName: '~Catalog~${catalog.name}~${imageDefinitionName}' + devBoxDefinition: { + imageReference: { + id: '${project.id}/images/~Catalog~${catalog.name}~${imageDefinitionName}' + } + sku: { + name: vmSku + } } + networkConnectionName: networkConnectionName + licenseType: 'Windows_Client' + localAdministrator: 'Enabled' + singleSignOnStatus: 'Enabled' + displayName: name + virtualNetworkType: networkType + managedVirtualNetworkRegions: (networkType == 'Managed') ? [resourceGroup().location] : [] } - networkConnectionName: networkConnectionName - licenseType: 'Windows_Client' - localAdministrator: 'Enabled' - singleSignOnStatus: 'Enabled' - displayName: name - virtualNetworkType: networkType - managedVirtualNetworkRegions: (networkType == 'Managed') ? [resourceGroup().location] : [] } -} - -@description('The name of the pool') -output poolName string = pool.name +] diff --git a/src/workload/workload.bicep b/src/workload/workload.bicep index 277f0f72..113a4322 100644 --- a/src/workload/workload.bicep +++ b/src/workload/workload.bicep @@ -60,7 +60,7 @@ module projects 'project/project.bicep' = [ logAnalyticsId: logAnalyticsId projectDescription: project.description ?? project.name devCenterName: devcenter.outputs.AZURE_DEV_CENTER_NAME - projectCatalogs: project.catalogs + catalogs: project.catalogs projectEnvironmentTypes: project.environmentTypes projectPools: project.pools projectNetwork: project.network