quickstarts/microsoft.devcenter/devbox-ready-to-code-image/tools/artifacts/windows-configure-user-tasks/windows-configure-user-tasks.ps1 (87 lines of code) (raw):

<# .DESCRIPTION Configures a set of tasks that will run when a user logs into a VM. #> param( [Parameter(Mandatory = $false)][string] $FirstLogonTasksBase64 ) $ErrorActionPreference = "Stop" Set-StrictMode -Version Latest function GetTaskID { param ( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSObject] $taskObj ) # By default use task's name as its ID which means that only a single (last) instance of such task will be executed on when user logs on $taskId = $taskObj.Task if ($taskObj.PSobject.Properties.Name -contains 'UniqueID') { $taskId = $taskObj.UniqueID } return $taskId } try { $setupDir = "c:\.tools\Setup" $setupScriptsDir = "$setupDir\Scripts" $logsDir = "$setupDir\Logs" if (Test-Path -Path $setupScriptsDir) { Write-Host "=== To avoid scripts versioning issues remove $setupScriptsDir in case it was created by the base image build" Remove-Item $setupScriptsDir -Recurse -Force -ErrorAction SilentlyContinue } Write-Host "=== Create $setupScriptsDir before copying scripts there" mkdir $setupScriptsDir -Force mkdir $logsDir -Force Write-Host "=== Copy setup scripts to $setupScriptsDir" @( (Join-Path $PSScriptRoot 'customization-utils.psm1') (Join-Path $PSScriptRoot 'setup-user-tasks.ps1') (Join-Path $PSScriptRoot 'run-firstlogon-tasks.ps1') (Join-Path $PSScriptRoot 'runonce-user-tasks.ps1') (Join-Path $(Split-Path -Parent $PSScriptRoot) '_common/windows-retry-utils.psm1') (Join-Path $(Split-Path -Parent $PSScriptRoot) '_common/windows-run-program.psm1') ) | ForEach-Object { Copy-Item $_ $setupScriptsDir -Force } Copy-Item "$PSScriptRoot\FirstLogonTasks" "$setupScriptsDir\FirstLogonTasks" -Recurse -Force -Exclude '*.Tests.ps1' Get-ChildItem -Recurse -File -Path $setupScriptsDir | Select-Object -First 100 # Hook the event invoked when Azure VM starts for the first time # - https://matt.kotsenas.com/posts/azure-setupcomplete2 # - https://learn.microsoft.com/en-us/dynamics-nav/setupcomplete2.cmd-file-example # - https://learn.microsoft.com/en-us/previous-versions/dynamicsnav-2018-developer/How-to--Create-a-Microsoft-Azure-Virtual-Machine-Operating-System-Image-for-Microsoft-Dynamics-NAV Write-Host "=== Configure Azure VM first startup event" $vmStartupScriptsDir = 'C:\Windows\OEM' $vmStartupScript = "$vmStartupScriptsDir\SetupComplete2.cmd" $vmOrigStartupScript = "$vmStartupScriptsDir\SetupComplete2FromOrigBaseImage.cmd" # If the base image for this VM was not created by Dev Box image templates then preserve the original SetupComplete2.cmd if ((!(Test-Path -Path $vmOrigStartupScript)) -and (Test-Path -Path $vmStartupScript) ) { Write-Host "=== Save SetupComplete2.cmd from the original base image to $vmOrigStartupScript" Move-Item $vmStartupScript $vmOrigStartupScript } mkdir $vmStartupScriptsDir -ErrorAction SilentlyContinue Copy-Item (Join-Path $PSScriptRoot 'SetupComplete2.cmd') $vmStartupScriptsDir -Force $firstLogonTasksFile = "$setupDir\FirstLogonTasks.json" if (!([string]::IsNullOrWhiteSpace($FirstLogonTasksBase64))) { $firstLogonTasks = [Text.Encoding]::ASCII.GetString([Convert]::FromBase64String($FirstLogonTasksBase64)) | ConvertFrom-Json $baseImageLogonTasks = @() if (Test-Path -Path $firstLogonTasksFile -PathType Leaf) { Write-Host "=== Found following logon tasks configured for the base image in $firstLogonTasksFile" Get-Content $firstLogonTasksFile $baseImageLogonTasks = Get-Content $firstLogonTasksFile -Raw | ConvertFrom-Json } # Only keep unique tasks that were configured for the base image $uniqueBaseImageLogonTasks = @() foreach ($baseImageLogonTask in $baseImageLogonTasks) { $baseImageTaskID = GetTaskID $baseImageLogonTask if ($null -eq ($firstLogonTasks | Where-Object { (GetTaskID $_) -eq $baseImageTaskID })) { $uniqueBaseImageLogonTasks += $baseImageLogonTask } else { Write-Host "== Skipped base image task $($baseImageLogonTask | ConvertTo-Json -Depth 10)" } } $firstLogonTasks = $uniqueBaseImageLogonTasks + $firstLogonTasks # Always use -Depth with ConvertTo-Json to preserve object structure (otherwise arrays fo example are turned into space separated strings) $firstLogonTasks | ConvertTo-Json -Depth 10 | Out-File -FilePath $firstLogonTasksFile Write-Host "=== Saved following tasks to run on user first logon to $firstLogonTasksFile" Get-Content $firstLogonTasksFile } } catch { Write-Error "!!! [ERROR] Unhandled exception:`n$_`n$($_.ScriptStackTrace)" -ErrorAction Stop }