From 8fdc6954cd23764eaadac5e8c51c07acf3a7ae88 Mon Sep 17 00:00:00 2001 From: AlexanderSehr Date: Thu, 26 Dec 2024 21:36:20 +0100 Subject: [PATCH 01/17] Ported a few updates --- .../deploymentFiles/sbx.image.bicep | 111 +++++++++--------- .../windows/Initialize-WindowsSoftware.ps1 | 3 + 2 files changed, 58 insertions(+), 56 deletions(-) diff --git a/constructs/azureImageBuilder/deploymentFiles/sbx.image.bicep b/constructs/azureImageBuilder/deploymentFiles/sbx.image.bicep index cf778781..1c17e120 100644 --- a/constructs/azureImageBuilder/deploymentFiles/sbx.image.bicep +++ b/constructs/azureImageBuilder/deploymentFiles/sbx.image.bicep @@ -21,6 +21,11 @@ param waitForImageBuild bool = true ///////////////////////////// // Template Deployment // ///////////////////////////// +var computeGalleryImageDefinitionName = 'sid-linux' +var assetsStorageAccountName = '' +var assetsStorageAccountContainerName = 'aibscripts' +var installPwshScriptName = 'Install-LinuxPowerShell.sh' +var initializeSoftwareScriptName = 'Initialize-LinuxSoftware.ps1' module imageDeployment '../templates/image.deploy.bicep' = { name: '${uniqueString(deployment().name, resourceLocation)}-image-sbx' @@ -28,13 +33,12 @@ module imageDeployment '../templates/image.deploy.bicep' = { resourceLocation: resourceLocation deploymentsToPerform: deploymentsToPerform computeGalleryName: '' - computeGalleryImageDefinitionName: 'sid-linux' + computeGalleryImageDefinitionName: computeGalleryImageDefinitionName waitForImageBuild: waitForImageBuild computeGalleryImageDefinitions: [ { - name: 'sid-linux' + name: computeGalleryImageDefinitionName osType: 'Linux' - identifier: { publisher: 'devops' offer: 'devops_linux' @@ -45,26 +49,18 @@ module imageDeployment '../templates/image.deploy.bicep' = { } ] - assetsStorageAccountName: '' - assetsStorageAccountContainerName: 'aibscripts' + assetsStorageAccountName: assetsStorageAccountName + assetsStorageAccountContainerName: assetsStorageAccountContainerName storageAccountFilesToUpload: [ { - name: 'Install-LinuxPowerShell.sh' - value: loadTextContent('../scripts/uploads/linux/Install-LinuxPowerShell.sh') + name: installPwshScriptName + value: loadTextContent('../scripts/uploads/linux/${installPwshScriptName}') } { - name: 'Initialize-LinuxSoftware.ps1' - value: loadTextContent('../scripts/uploads/linux/Initialize-LinuxSoftware.ps1') + name: initializeSoftwareScriptName + value: loadTextContent('../scripts/uploads/linux/${initializeSoftwareScriptName}') } - // { - // name: 'Install-WindowsPowerShell.ps1' - // value: loadTextContent('../scripts/uploads/windows/Install-WindowsPowerShell.ps1') - // } - // { - // name: 'Initialize-WindowsSoftware.ps1' - // value: loadTextContent('../scripts/uploads/windows/Initialize-WindowsSoftware.ps1') - // } ] // Linux Example imageTemplateImageSource: { @@ -75,25 +71,25 @@ module imageDeployment '../templates/image.deploy.bicep' = { version: 'latest' // Custom image example // type: 'SharedImageVersion' - // imageVersionID: '${subscription().id}/resourceGroups/myRg/providers/Microsoft.Compute/galleries//images/sid-linux/versions/0.24470.675' + // imageVersionID: '${subscription().id}/resourceGroups/myRg/providers/Microsoft.Compute/galleries//images/${computeGalleryImageDefinitionName}/versions/0.24470.675' } imageTemplateCustomizationSteps: [ { type: 'Shell' name: 'PowerShell installation' - scriptUri: 'https://.blob.${az.environment().suffixes.storage}/aibscripts/Install-LinuxPowerShell.sh' + scriptUri: 'https://${assetsStorageAccountName}.blob.${az.environment().suffixes.storage}/${assetsStorageAccountContainerName}/${installPwshScriptName}' } { type: 'File' - name: 'Initialize-LinuxSoftware' - sourceUri: 'https://.blob.${az.environment().suffixes.storage}/aibscripts/Initialize-LinuxSoftware.ps1' - destination: 'Initialize-LinuxSoftware.ps1' + name: 'Download ${initializeSoftwareScriptName}' + sourceUri: 'https://${assetsStorageAccountName}.blob.${az.environment().suffixes.storage}/${assetsStorageAccountContainerName}/${initializeSoftwareScriptName}' + destination: initializeSoftwareScriptName } { type: 'Shell' name: 'Software installation' inline: [ - 'pwsh \'Initialize-LinuxSoftware.ps1\'' + 'pwsh \'${initializeSoftwareScriptName}\'' ] } ] @@ -101,7 +97,7 @@ module imageDeployment '../templates/image.deploy.bicep' = { // Windows Example // computeGalleryImageDefinitions: [ // { - // name: 'sid-windows' + // name: computeGalleryImageDefinitionName // osType: 'Windows' // identifier: { // publisher: 'devops' @@ -109,43 +105,46 @@ module imageDeployment '../templates/image.deploy.bicep' = { // sku: 'devops_windows_az' // } // osState: 'Generalized' + // hyperVGeneration: 'V2' // } // ] - // imageTemplateComputeGalleryImageDefinitionName: 'sid-windows' + // storageAccountFilesToUpload: [ + // { + // name: installPwshScriptName + // value: loadTextContent('../scripts/uploads/windows/${installPwshScriptName}') + // } + // { + // name: initializeSoftwareScriptName + // value: loadTextContent('../scripts/uploads/windows/${initializeSoftwareScriptName}') + // } + // ] + // imageTemplateComputeGalleryImageDefinitionName: computeGalleryImageDefinitionName // imageTemplateImageSource: { - // type: 'PlatformImage' - // publisher: 'MicrosoftWindowsDesktop' - // offer: 'Windows-10' - // sku: '19h2-evd' - // version: 'latest' + // type: 'PlatformImage' + // publisher: 'MicrosoftWindowsDesktop' + // offer: 'Windows-11' + // sku: 'win11-24h2-avd' + // version: 'latest' // } // imageTemplateCustomizationSteps: [ - // { - // type: 'PowerShell' - // name: 'PowerShell installation' - // inline: [ - // 'Write-Output "Download"' - // 'wget \'https://.blob.${environment().suffixes.storage}/aibscripts/Install-WindowsPowerShell.ps1?\' -O \'Install-WindowsPowerShell.ps1\'' - // 'Write-Output "Invocation"' - // '. \'Install-WindowsPowerShell.ps1\'' - // ] - // runElevated: true - // } - // { - // type: 'File' - // name: 'Initialize-WindowsSoftware' - // sourceUri: 'https://.blob.${az.environment().suffixes.storage}/aibscripts/Initialize-WindowsSoftware.ps1' - // destination: 'Initialize-WindowsSoftware.ps1' - // } - // { - // type: 'PowerShell' - // name: 'Software installation' - // inline: [ - // 'wget \'https://.blob.${environment().suffixes.storage}/aibscripts/Initialize-WindowsSoftware.ps1?\' -O \'Initialize-WindowsSoftware.ps1\'' - // 'pwsh \'Initialize-WindowsSoftware.ps1\'' - // ] - // runElevated: true - // } + // { + // type: 'PowerShell' + // name: 'PowerShell Core installation' + // scriptUri: 'https://${assetsStorageAccountName}.blob.${environment().suffixes.storage}/${assetsStorageAccountContainerName}/${installPwshScriptName}' + // } + // { + // type: 'File' + // name: 'Download ${initializeSoftwareScriptName}' + // sourceUri: 'https://${assetsStorageAccountName}.blob.${environment().suffixes.storage}/${assetsStorageAccountContainerName}/${initializeSoftwareScriptName}' + // destination: initializeSoftwareScriptName + // } + // { + // type: 'PowerShell' + // name: 'Software installation' + // inline: [ + // 'pwsh \'${initializeSoftwareScriptName}\'' + // ] + // } // ] } } diff --git a/constructs/azureImageBuilder/scripts/uploads/windows/Initialize-WindowsSoftware.ps1 b/constructs/azureImageBuilder/scripts/uploads/windows/Initialize-WindowsSoftware.ps1 index fe2043f6..e20ee63f 100644 --- a/constructs/azureImageBuilder/scripts/uploads/windows/Initialize-WindowsSoftware.ps1 +++ b/constructs/azureImageBuilder/scripts/uploads/windows/Initialize-WindowsSoftware.ps1 @@ -622,6 +622,9 @@ Set-ExecutionPolicy Bypass -Scope Process -Force ####################### LogInfo('Install-Choco start') $null = Install-Choco + +LogInfo("Enable Choco's global confirmation") +choco feature enable -n allowGlobalConfirmation # Enable automatic confirmation for installations LogInfo('Install-Choco end') ########################## From 74fb16a9cd342b02a7df86315fc0e76af8e8fe40 Mon Sep 17 00:00:00 2001 From: AlexanderSehr Date: Fri, 27 Dec 2024 22:33:43 +0100 Subject: [PATCH 02/17] Added windows/linux switch --- .../.templates/pipeline.jobs.yml | 4 +- .azuredevops/azureImageBuilder/pipeline.yml | 10 ++ ...{sbx.image.bicep => sbx.image.linux.bicep} | 53 ----------- .../deploymentFiles/sbx.image.windows.bicep | 94 +++++++++++++++++++ 4 files changed, 107 insertions(+), 54 deletions(-) rename constructs/azureImageBuilder/deploymentFiles/{sbx.image.bicep => sbx.image.linux.bicep} (62%) create mode 100644 constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep diff --git a/.azuredevops/azureImageBuilder/.templates/pipeline.jobs.yml b/.azuredevops/azureImageBuilder/.templates/pipeline.jobs.yml index 31d517bd..1d705176 100644 --- a/.azuredevops/azureImageBuilder/.templates/pipeline.jobs.yml +++ b/.azuredevops/azureImageBuilder/.templates/pipeline.jobs.yml @@ -3,6 +3,8 @@ parameters: default: - name: deploymentsToPerform default: + - name: osType + default: - name: waitForImageBuild default: - name: removeImageTemplateResources @@ -186,7 +188,7 @@ jobs: # INVOKE DEPLOYMENT # # ----------------- # $functionInput = @{ - TemplateFilePath = Join-Path '$(deploymentFilesPath)' '${{ parameters.environment }}.image.bicep' + TemplateFilePath = Join-Path '$(deploymentFilesPath)' '${{ parameters.environment }}.image.${{ parameters.osType }}.bicep' DeploymentMetadataLocation = '$(deploymentMetadataLocation)' SubscriptionId = (Get-AzContext).Subscription.Id RepoRoot = '$(System.DefaultWorkingDirectory)' diff --git a/.azuredevops/azureImageBuilder/pipeline.yml b/.azuredevops/azureImageBuilder/pipeline.yml index aa143e6d..c5ea929c 100644 --- a/.azuredevops/azureImageBuilder/pipeline.yml +++ b/.azuredevops/azureImageBuilder/pipeline.yml @@ -19,6 +19,13 @@ parameters: - Only base - Only assets & image - Only image + - name: osType + displayName: OS to create the image for + type: string + default: Linux + values: + - Linux + - Windows - name: waitForImageBuild displayName: Wait for image build type: boolean @@ -41,6 +48,7 @@ stages: - template: .templates/pipeline.jobs.yml parameters: environment: 'sbx' + osType: '${{ lower(parameters.osType) }}' deploymentsToPerform: '${{ parameters.deploymentsToPerform }}' waitForImageBuild: '${{ parameters.waitForImageBuild }}' removeImageTemplateResources: '${{ parameters.removeImageTemplateResources }}' @@ -57,6 +65,7 @@ stages: - template: .templates/pipeline.jobs.yml parameters: environment: 'dev' + osType: '${{ lower(parameters.osType) }}' deploymentsToPerform: '${{ parameters.deploymentsToPerform }}' waitForImageBuild: '${{ parameters.waitForImageBuild }}' removeImageTemplateResources: '${{ parameters.removeImageTemplateResources }}' @@ -74,6 +83,7 @@ stages: - template: .templates/pipeline.jobs.yml parameters: environment: 'prd' + osType: '${{ lower(parameters.osType) }}' deploymentsToPerform: '${{ parameters.deploymentsToPerform }}' waitForImageBuild: '${{ parameters.waitForImageBuild }}' removeImageTemplateResources: '${{ parameters.removeImageTemplateResources }}' diff --git a/constructs/azureImageBuilder/deploymentFiles/sbx.image.bicep b/constructs/azureImageBuilder/deploymentFiles/sbx.image.linux.bicep similarity index 62% rename from constructs/azureImageBuilder/deploymentFiles/sbx.image.bicep rename to constructs/azureImageBuilder/deploymentFiles/sbx.image.linux.bicep index 1c17e120..17a3e18c 100644 --- a/constructs/azureImageBuilder/deploymentFiles/sbx.image.bicep +++ b/constructs/azureImageBuilder/deploymentFiles/sbx.image.linux.bicep @@ -93,58 +93,5 @@ module imageDeployment '../templates/image.deploy.bicep' = { ] } ] - - // Windows Example - // computeGalleryImageDefinitions: [ - // { - // name: computeGalleryImageDefinitionName - // osType: 'Windows' - // identifier: { - // publisher: 'devops' - // offer: 'devops_windows' - // sku: 'devops_windows_az' - // } - // osState: 'Generalized' - // hyperVGeneration: 'V2' - // } - // ] - // storageAccountFilesToUpload: [ - // { - // name: installPwshScriptName - // value: loadTextContent('../scripts/uploads/windows/${installPwshScriptName}') - // } - // { - // name: initializeSoftwareScriptName - // value: loadTextContent('../scripts/uploads/windows/${initializeSoftwareScriptName}') - // } - // ] - // imageTemplateComputeGalleryImageDefinitionName: computeGalleryImageDefinitionName - // imageTemplateImageSource: { - // type: 'PlatformImage' - // publisher: 'MicrosoftWindowsDesktop' - // offer: 'Windows-11' - // sku: 'win11-24h2-avd' - // version: 'latest' - // } - // imageTemplateCustomizationSteps: [ - // { - // type: 'PowerShell' - // name: 'PowerShell Core installation' - // scriptUri: 'https://${assetsStorageAccountName}.blob.${environment().suffixes.storage}/${assetsStorageAccountContainerName}/${installPwshScriptName}' - // } - // { - // type: 'File' - // name: 'Download ${initializeSoftwareScriptName}' - // sourceUri: 'https://${assetsStorageAccountName}.blob.${environment().suffixes.storage}/${assetsStorageAccountContainerName}/${initializeSoftwareScriptName}' - // destination: initializeSoftwareScriptName - // } - // { - // type: 'PowerShell' - // name: 'Software installation' - // inline: [ - // 'pwsh \'${initializeSoftwareScriptName}\'' - // ] - // } - // ] } } diff --git a/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep b/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep new file mode 100644 index 00000000..f62e1c86 --- /dev/null +++ b/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep @@ -0,0 +1,94 @@ +targetScope = 'subscription' + +////////////////////////// +// Input Parameters // +////////////////////////// +@description('Optional. A parameter to control which deployments should be executed') +@allowed([ + 'All' + 'Only base' + 'Only assets & image' + 'Only image' +]) +param deploymentsToPerform string = 'All' + +@description('Optional. Specifies the location for resources.') +param resourceLocation string = 'NorthEurope' + +@description('Optional. A parameter to control if the deployment should wait for the image build to complete.') +param waitForImageBuild bool = true + +///////////////////////////// +// Template Deployment // +///////////////////////////// +var computeGalleryImageDefinitionName = 'sid-windows' +var assetsStorageAccountName = '' +var assetsStorageAccountContainerName = 'aibscripts' +var installPwshScriptName = 'Install-WindowsPowerShell.ps1' +var initializeSoftwareScriptName = 'Initialize-WindowsSoftware.ps1' + +module imageDeployment '../templates/image.deploy.bicep' = { + name: '${uniqueString(deployment().name, resourceLocation)}-image-sbx' + params: { + resourceLocation: resourceLocation + deploymentsToPerform: deploymentsToPerform + computeGalleryName: '' + computeGalleryImageDefinitionName: computeGalleryImageDefinitionName + waitForImageBuild: waitForImageBuild + + assetsStorageAccountName: assetsStorageAccountName + assetsStorageAccountContainerName: assetsStorageAccountContainerName + + // Windows Example + computeGalleryImageDefinitions: [ + { + name: computeGalleryImageDefinitionName + osType: 'Windows' + identifier: { + publisher: 'devops' + offer: 'devops_windows' + sku: 'devops_windows_az' + } + osState: 'Generalized' + hyperVGeneration: 'V2' + } + ] + storageAccountFilesToUpload: [ + { + name: installPwshScriptName + value: loadTextContent('../scripts/uploads/windows/${installPwshScriptName}') + } + { + name: initializeSoftwareScriptName + value: loadTextContent('../scripts/uploads/windows/${initializeSoftwareScriptName}') + } + ] + imageTemplateImageSource: { + type: 'PlatformImage' + publisher: 'MicrosoftWindowsDesktop' + offer: 'Windows-11' + sku: 'win11-24h2-avd' + version: 'latest' + } + imageTemplateCustomizationSteps: [ + { + type: 'PowerShell' + name: 'PowerShell Core installation' + scriptUri: 'https://${assetsStorageAccountName}.blob.${environment().suffixes.storage}/${assetsStorageAccountContainerName}/${installPwshScriptName}' + } + { + type: 'File' + name: 'Download ${initializeSoftwareScriptName}' + sourceUri: 'https://${assetsStorageAccountName}.blob.${environment().suffixes.storage}/${assetsStorageAccountContainerName}/${initializeSoftwareScriptName}' + destination: initializeSoftwareScriptName + } + { + type: 'PowerShell' + name: 'Software installation' + inline: [ + 'pwsh \'${initializeSoftwareScriptName}\'' + ] + } + ] + } +} From 8bdfeed925c614620369609f0a204263ec1dd134 Mon Sep 17 00:00:00 2001 From: AlexanderSehr Date: Fri, 27 Dec 2024 22:36:58 +0100 Subject: [PATCH 03/17] Update to latest --- .azuredevops/azureImageBuilder/variables.yml | 18 +++++++++--------- .azuredevops/managedDevOpsPool/variables.yml | 18 +++++++++--------- .../deploymentFiles/sbx.image.linux.bicep | 6 +++--- .../deploymentFiles/sbx.image.windows.bicep | 4 ++-- .../deploymentFiles/sbx.pool.bicep | 2 +- ...ting images with the Azure Image Builder.md | 4 ++-- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/.azuredevops/azureImageBuilder/variables.yml b/.azuredevops/azureImageBuilder/variables.yml index 4d380202..04a0e9f9 100644 --- a/.azuredevops/azureImageBuilder/variables.yml +++ b/.azuredevops/azureImageBuilder/variables.yml @@ -12,17 +12,17 @@ variables: ## GENERAL ## ############# #region shared - vmImage_sbx: 'ubuntu-latest' # Use this for microsoft-hosted agents - vmImage_dev: 'ubuntu-latest' # Use this for microsoft-hosted agents - vmImage_prd: 'ubuntu-latest' # Use this for microsoft-hosted agents + vmImage_sbx: '' # 'ubuntu-latest' # Use this for microsoft-hosted agents + vmImage_dev: '' # 'ubuntu-latest' # Use this for microsoft-hosted agents + vmImage_prd: '' # 'ubuntu-latest' # Use this for microsoft-hosted agents - poolName_sbx: '' # Use this for self-hosted agents - poolName_dev: '' # Use this for self-hosted agents - poolName_prd: '' # Use this for self-hosted agents + poolName_sbx: 'core-vmss' # Use this for self-hosted agents + poolName_dev: 'core-vmss' # Use this for self-hosted agents + poolName_prd: 'core-vmss' # Use this for self-hosted agents - serviceConnection_sbx: '' - serviceConnection_dev: '' - serviceConnection_prd: '' + serviceConnection_sbx: 'OIDC-Private' + serviceConnection_dev: 'OIDC-Private' + serviceConnection_prd: 'OIDC-Private' #endregion #region specific diff --git a/.azuredevops/managedDevOpsPool/variables.yml b/.azuredevops/managedDevOpsPool/variables.yml index 6e594465..b5a27e87 100644 --- a/.azuredevops/managedDevOpsPool/variables.yml +++ b/.azuredevops/managedDevOpsPool/variables.yml @@ -12,17 +12,17 @@ variables: ## GENERAL ## ############# #region shared - vmImage_sbx: 'ubuntu-latest' # Use this for microsoft-hosted agents - vmImage_dev: 'ubuntu-latest' # Use this for microsoft-hosted agents - vmImage_prd: 'ubuntu-latest' # Use this for microsoft-hosted agents + vmImage_sbx: '' # 'ubuntu-latest' # Use this for microsoft-hosted agents + vmImage_dev: '' # 'ubuntu-latest' # Use this for microsoft-hosted agents + vmImage_prd: '' # 'ubuntu-latest' # Use this for microsoft-hosted agents - poolName_sbx: '' # Use this for self-hosted agents - poolName_dev: '' # Use this for self-hosted agents - poolName_prd: '' # Use this for self-hosted agents + poolName_sbx: 'core-vmss' # Use this for self-hosted agents + poolName_dev: 'core-vmss' # Use this for self-hosted agents + poolName_prd: 'core-vmss' # Use this for self-hosted agents - serviceConnection_sbx: '' - serviceConnection_dev: '' - serviceConnection_prd: '' + serviceConnection_sbx: 'OIDC-Private' + serviceConnection_dev: 'OIDC-Private' + serviceConnection_prd: 'OIDC-Private' #endregion #region specific diff --git a/constructs/azureImageBuilder/deploymentFiles/sbx.image.linux.bicep b/constructs/azureImageBuilder/deploymentFiles/sbx.image.linux.bicep index 17a3e18c..c58b266d 100644 --- a/constructs/azureImageBuilder/deploymentFiles/sbx.image.linux.bicep +++ b/constructs/azureImageBuilder/deploymentFiles/sbx.image.linux.bicep @@ -22,7 +22,7 @@ param waitForImageBuild bool = true // Template Deployment // ///////////////////////////// var computeGalleryImageDefinitionName = 'sid-linux' -var assetsStorageAccountName = '' +var assetsStorageAccountName = 'alsehrst' var assetsStorageAccountContainerName = 'aibscripts' var installPwshScriptName = 'Install-LinuxPowerShell.sh' var initializeSoftwareScriptName = 'Initialize-LinuxSoftware.ps1' @@ -32,7 +32,7 @@ module imageDeployment '../templates/image.deploy.bicep' = { params: { resourceLocation: resourceLocation deploymentsToPerform: deploymentsToPerform - computeGalleryName: '' + computeGalleryName: 'alsehrcg' computeGalleryImageDefinitionName: computeGalleryImageDefinitionName waitForImageBuild: waitForImageBuild computeGalleryImageDefinitions: [ @@ -71,7 +71,7 @@ module imageDeployment '../templates/image.deploy.bicep' = { version: 'latest' // Custom image example // type: 'SharedImageVersion' - // imageVersionID: '${subscription().id}/resourceGroups/myRg/providers/Microsoft.Compute/galleries//images/${computeGalleryImageDefinitionName}/versions/0.24470.675' + // imageVersionID: '${subscription().id}/resourceGroups/myRg/providers/Microsoft.Compute/galleries/alsehrcg/images/${computeGalleryImageDefinitionName}/versions/0.24470.675' } imageTemplateCustomizationSteps: [ { diff --git a/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep b/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep index f62e1c86..8b4112fe 100644 --- a/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep +++ b/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep @@ -22,7 +22,7 @@ param waitForImageBuild bool = true // Template Deployment // ///////////////////////////// var computeGalleryImageDefinitionName = 'sid-windows' -var assetsStorageAccountName = '' +var assetsStorageAccountName = 'alsehrst' var assetsStorageAccountContainerName = 'aibscripts' var installPwshScriptName = 'Install-WindowsPowerShell.ps1' var initializeSoftwareScriptName = 'Initialize-WindowsSoftware.ps1' @@ -32,7 +32,7 @@ module imageDeployment '../templates/image.deploy.bicep' = { params: { resourceLocation: resourceLocation deploymentsToPerform: deploymentsToPerform - computeGalleryName: '' + computeGalleryName: 'alsehrcg' computeGalleryImageDefinitionName: computeGalleryImageDefinitionName waitForImageBuild: waitForImageBuild diff --git a/constructs/managedDevOpsPool/deploymentFiles/sbx.pool.bicep b/constructs/managedDevOpsPool/deploymentFiles/sbx.pool.bicep index 29f8b764..2994997e 100644 --- a/constructs/managedDevOpsPool/deploymentFiles/sbx.pool.bicep +++ b/constructs/managedDevOpsPool/deploymentFiles/sbx.pool.bicep @@ -14,7 +14,7 @@ module managedDevOpsPoolDeployment '../templates/pool.deploy.bicep' = { name: '${uniqueString(deployment().name, resourceLocation)}-managedPool-sbx' params: { resourceLocation: resourceLocation - computeGalleryName: '' + computeGalleryName: 'alsehrcg' computeGalleryImageDefinitionName: 'sid-linux' devCenterName: 'my-center' devCenterProjectName: 'my-project' diff --git a/docs/wiki/Creating images with the Azure Image Builder.md b/docs/wiki/Creating images with the Azure Image Builder.md index ae1c1877..40f01fe6 100644 --- a/docs/wiki/Creating images with the Azure Image Builder.md +++ b/docs/wiki/Creating images with the Azure Image Builder.md @@ -138,13 +138,13 @@ imageTemplateCustomizationSteps: [ { type: 'Shell' name: 'PowerShell installation' - scriptUri: 'https://.blob.core.windows.net/aibscripts/Install-LinuxPowerShell.sh' + scriptUri: 'https://alsehrst.blob.core.windows.net/aibscripts/Install-LinuxPowerShell.sh' } { type: 'Shell' name: 'Prepare software installation' inline: [ - 'wget \'https://.blob.core.windows.net/aibscripts/Initialize-LinuxSoftware.ps1\' -O \'Initialize-LinuxSoftware.ps1\'' + 'wget \'https://alsehrst.blob.core.windows.net/aibscripts/Initialize-LinuxSoftware.ps1\' -O \'Initialize-LinuxSoftware.ps1\'' 'sed -i \'s/\r$//' 'Initialize-LinuxSoftware.ps1\'' 'pwsh \'Initialize-LinuxSoftware.ps1\'' ] From f975011d0dd060f514e85c37b72d9c79afc464fd Mon Sep 17 00:00:00 2001 From: AlexanderSehr Date: Fri, 27 Dec 2024 22:39:45 +0100 Subject: [PATCH 04/17] Update to latest --- .../managedDevOpsPool/deploymentFiles/sbx.pool.bicep | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/constructs/managedDevOpsPool/deploymentFiles/sbx.pool.bicep b/constructs/managedDevOpsPool/deploymentFiles/sbx.pool.bicep index 2994997e..6bc75286 100644 --- a/constructs/managedDevOpsPool/deploymentFiles/sbx.pool.bicep +++ b/constructs/managedDevOpsPool/deploymentFiles/sbx.pool.bicep @@ -18,12 +18,12 @@ module managedDevOpsPoolDeployment '../templates/pool.deploy.bicep' = { computeGalleryImageDefinitionName: 'sid-linux' devCenterName: 'my-center' devCenterProjectName: 'my-project' - organizationName: '' - projectNames: [''] - poolName: '' + organizationName: 'asehr' + projectNames: ['Onyx'] + poolName: 'onyx-pool' poolMaximumConcurrency: 5 // Tenant-specific 'DevOpsInfrastructure' Enterprise Application objectId. // Can be fetched by running `(Get-AzAdServicePrincipal -DisplayName 'DevOpsInfrastructure').Id` while logged into the tenant to deploy into. - devOpsInfrastructureEnterpriseApplicationObjectId: '' + devOpsInfrastructureEnterpriseApplicationObjectId: 'a67e26cd-08dc-47be-8217-df02edb89ba8' } } From 0aec689d446ba7cd3512f4725c9058a45a47be16 Mon Sep 17 00:00:00 2001 From: AlexanderSehr Date: Fri, 27 Dec 2024 22:57:14 +0100 Subject: [PATCH 05/17] Update to latest --- .azuredevops/azureImageBuilder/.templates/pipeline.jobs.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.azuredevops/azureImageBuilder/.templates/pipeline.jobs.yml b/.azuredevops/azureImageBuilder/.templates/pipeline.jobs.yml index 1d705176..69a4361e 100644 --- a/.azuredevops/azureImageBuilder/.templates/pipeline.jobs.yml +++ b/.azuredevops/azureImageBuilder/.templates/pipeline.jobs.yml @@ -65,7 +65,7 @@ jobs: . (Join-Path '$(System.DefaultWorkingDirectory)' '$(orchestrationFunctionsPath)' 'image' 'Remove-ImageTemplate.ps1') $functionInput = @{ - TemplateFilePath = Join-Path '$(deploymentFilesPath)' '${{ parameters.environment }}.image.bicep' + TemplateFilePath = Join-Path '$(deploymentFilesPath)' '${{ parameters.environment }}.image.${{ parameters.osType }}.bicep' } Write-Verbose "Invoke task with" -Verbose @@ -91,7 +91,7 @@ jobs: . (Join-Path '$(System.DefaultWorkingDirectory)' '$(orchestrationFunctionsPath)' 'image' 'Remove-DeploymentScript.ps1') $functionInput = @{ - TemplateFilePath = Join-Path '$(deploymentFilesPath)' '${{ parameters.environment }}.image.bicep' + TemplateFilePath = Join-Path '$(deploymentFilesPath)' '${{ parameters.environment }}.image.${{ parameters.osType }}.bicep' } Write-Verbose "Invoke task with" -Verbose @@ -117,7 +117,7 @@ jobs: . (Join-Path '$(System.DefaultWorkingDirectory)' '$(orchestrationFunctionsPath)' 'image' 'Remove-ResourcesInStagingRg.ps1') $functionInput = @{ - TemplateFilePath = Join-Path '$(deploymentFilesPath)' '${{ parameters.environment }}.image.bicep' + TemplateFilePath = Join-Path '$(deploymentFilesPath)' '${{ parameters.environment }}.image.${{ parameters.osType }}.bicep' } Write-Verbose "Invoke task with" -Verbose From 0f2cef265b0246cd3aef0089823d8b5b46f37909 Mon Sep 17 00:00:00 2001 From: AlexanderSehr Date: Fri, 27 Dec 2024 23:39:55 +0100 Subject: [PATCH 06/17] Disabled deployment of staging RG for windows --- .../azureImageBuilder/deploymentFiles/sbx.image.windows.bicep | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep b/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep index 8b4112fe..38113484 100644 --- a/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep +++ b/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep @@ -39,7 +39,6 @@ module imageDeployment '../templates/image.deploy.bicep' = { assetsStorageAccountName: assetsStorageAccountName assetsStorageAccountContainerName: assetsStorageAccountContainerName - // Windows Example computeGalleryImageDefinitions: [ { name: computeGalleryImageDefinitionName @@ -70,6 +69,8 @@ module imageDeployment '../templates/image.deploy.bicep' = { sku: 'win11-24h2-avd' version: 'latest' } + + imageTemplateResourceGroupName: '' imageTemplateCustomizationSteps: [ { type: 'PowerShell' From d7bea7fab9c0702c68328dbf35c99e32eb5d4539 Mon Sep 17 00:00:00 2001 From: AlexanderSehr Date: Fri, 27 Dec 2024 23:45:04 +0100 Subject: [PATCH 07/17] Update to latest --- .../image/Remove-ResourcesInStagingRg.ps1 | 172 +++++++++--------- 1 file changed, 88 insertions(+), 84 deletions(-) diff --git a/constructs/azureImageBuilder/scripts/image/Remove-ResourcesInStagingRg.ps1 b/constructs/azureImageBuilder/scripts/image/Remove-ResourcesInStagingRg.ps1 index 7b2a6015..0d3af46f 100644 --- a/constructs/azureImageBuilder/scripts/image/Remove-ResourcesInStagingRg.ps1 +++ b/constructs/azureImageBuilder/scripts/image/Remove-ResourcesInStagingRg.ps1 @@ -1,84 +1,88 @@ -<# -.SYNOPSIS -Remove either the resources in the Image Template Resource Group or the whole Resource Group - -.DESCRIPTION -Remove either the resources in the Image Template Resource Group or the whole Resource Group - -.PARAMETER TemplateFilePath -Required. The path to the Template File to fetch the Image Tempalte information from that are used to identify and remove the correct Resource Group (resources). - -.PARAMETER RemoveImageTemplateResourceGroup -Optional. A switch to control whether to just remove the resources in the Image Template Staging Resource Group or the whole Resource Group. -Keeping the Resource Group may be relevant if one deployed an exception for this resource group to allow the Image Builder to create a storage account with a public endpoint even though a policy may require it to be disabled. - -.EXAMPLE -Remove-ResourcesInStagingRg -TemplateFilePath 'C:\dev\DevOps-Self-Hosted\constructs\azureImageBuilder\deploymentFiles\imageTemplate.bicep' - -Removes the resources in the Image Template Staging Resource Group, if any. -#> -function Remove-ResourcesInStagingRg { - - [CmdletBinding(SupportsShouldProcess)] - param ( - [Parameter(Mandatory = $true)] - [string] $TemplateFilePath, - - [Parameter(Mandatory = $false)] - [switch] $RemoveImageTemplateResourceGroup - ) - - begin { - # Load helper - $repoRoot = (Get-Item $PSScriptRoot).Parent.Parent.Parent.Parent.FullName - - . (Join-Path -Path $repoRoot 'sharedScripts' 'template' 'Get-TemplateParameterValue.ps1') - } - - process { - # Fetch information - # ----------------- - $templateParamInputObject = @{ - TemplateFilePath = $TemplateFilePath - ParameterName = @('imageTemplateResourceGroupName') - } - $imageTemplateResourceGroupName = Get-TemplateParameterValue @templateParamInputObject - - - if (-not (Get-AzResourceGroup $imageTemplateResourceGroupName -ErrorAction SilentlyContinue)) { - Write-Verbose ('Resource Group [{0}] does not exist. Skipping cleanup.' -f $imageTemplateResourceGroupName) -Verbose - return - } - - # Fetching & removing resources - # ============================= - if ($RemoveImageTemplateResourceGroup) { - Write-Verbose "Removing resource group [$imageTemplateResourceGroupName]" - if ($PSCmdlet.ShouldProcess('Resource [{0}]' -f $resource.Id, 'Remove')) { - $null = Remove-AzResourceGroup -Name $imageTemplateResourceGroupName -Force -ErrorAction 'SilentlyContinue' - } - return - } - - - $imageTemplateResources = Get-AzResource -ResourceGroupName $imageTemplateResourceGroupName - - if ($imageTemplateResources.Count -gt 0) { - - Write-Verbose 'Removing resources:' - foreach ($resource in $imageTemplateResources) { - Write-Verbose ('- [{0}]' -f $resource.id) - } - - foreach ($resource in $imageTemplateResources) { - if ($PSCmdlet.ShouldProcess('Resource [{0}]' -f $resource.Id, 'Remove')) { - $null = Remove-AzResource -ResourceId $resource.Id -Force -ErrorAction 'Stop' - } - } - } - } - - end { - Write-Debug ('{0} exited' -f $MyInvocation.MyCommand) - } -} +<# +.SYNOPSIS +Remove either the resources in the Image Template Resource Group or the whole Resource Group + +.DESCRIPTION +Remove either the resources in the Image Template Resource Group or the whole Resource Group + +.PARAMETER TemplateFilePath +Required. The path to the Template File to fetch the Image Tempalte information from that are used to identify and remove the correct Resource Group (resources). + +.PARAMETER RemoveImageTemplateResourceGroup +Optional. A switch to control whether to just remove the resources in the Image Template Staging Resource Group or the whole Resource Group. +Keeping the Resource Group may be relevant if one deployed an exception for this resource group to allow the Image Builder to create a storage account with a public endpoint even though a policy may require it to be disabled. + +.EXAMPLE +Remove-ResourcesInStagingRg -TemplateFilePath 'C:\dev\DevOps-Self-Hosted\constructs\azureImageBuilder\deploymentFiles\imageTemplate.bicep' + +Removes the resources in the Image Template Staging Resource Group, if any. +#> +function Remove-ResourcesInStagingRg { + + [CmdletBinding(SupportsShouldProcess)] + param ( + [Parameter(Mandatory = $true)] + [string] $TemplateFilePath, + + [Parameter(Mandatory = $false)] + [switch] $RemoveImageTemplateResourceGroup + ) + + begin { + # Load helper + $repoRoot = (Get-Item $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + + . (Join-Path -Path $repoRoot 'sharedScripts' 'template' 'Get-TemplateParameterValue.ps1') + } + + process { + # Fetch information + # ----------------- + $templateParamInputObject = @{ + TemplateFilePath = $TemplateFilePath + ParameterName = @('imageTemplateResourceGroupName') + } + $imageTemplateResourceGroupName = Get-TemplateParameterValue @templateParamInputObject + + if ([String]::IsNullOrEmpty($imageTemplateResourceGroupName)) { + Write-Verbose 'No Image Template Resource Group defined. Skipping cleanup.' + return + } + + if (-not (Get-AzResourceGroup $imageTemplateResourceGroupName -ErrorAction SilentlyContinue)) { + Write-Verbose ('Resource Group [{0}] does not exist. Skipping cleanup.' -f $imageTemplateResourceGroupName) -Verbose + return + } + + # Fetching & removing resources + # ============================= + if ($RemoveImageTemplateResourceGroup) { + Write-Verbose "Removing resource group [$imageTemplateResourceGroupName]" + if ($PSCmdlet.ShouldProcess('Resource [{0}]' -f $resource.Id, 'Remove')) { + $null = Remove-AzResourceGroup -Name $imageTemplateResourceGroupName -Force -ErrorAction 'SilentlyContinue' + } + return + } + + + $imageTemplateResources = Get-AzResource -ResourceGroupName $imageTemplateResourceGroupName + + if ($imageTemplateResources.Count -gt 0) { + + Write-Verbose 'Removing resources:' + foreach ($resource in $imageTemplateResources) { + Write-Verbose ('- [{0}]' -f $resource.id) + } + + foreach ($resource in $imageTemplateResources) { + if ($PSCmdlet.ShouldProcess('Resource [{0}]' -f $resource.Id, 'Remove')) { + $null = Remove-AzResource -ResourceId $resource.Id -Force -ErrorAction 'Stop' + } + } + } + } + + end { + Write-Debug ('{0} exited' -f $MyInvocation.MyCommand) + } +} From 6da959b477efc654a9b29221cf91d275d9a4d381 Mon Sep 17 00:00:00 2001 From: AlexanderSehr Date: Fri, 27 Dec 2024 23:51:12 +0100 Subject: [PATCH 08/17] Update to latest --- constructs/azureImageBuilder/templates/image.deploy.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constructs/azureImageBuilder/templates/image.deploy.bicep b/constructs/azureImageBuilder/templates/image.deploy.bicep index d08460dd..46a52ca5 100644 --- a/constructs/azureImageBuilder/templates/image.deploy.bicep +++ b/constructs/azureImageBuilder/templates/image.deploy.bicep @@ -106,7 +106,7 @@ param deploymentsToPerform string = 'Only assets & image' // Deployments // // =========== // -module imageConstruct 'br/public:avm/ptn/virtual-machine-images/azure-image-builder:0.1.1' = { +module imageConstruct 'br/public:avm/ptn/virtual-machine-images/azure-image-builder:0.1.2' = { name: '${uniqueString(deployment().name, resourceLocation)}-image-construct' params: { deploymentsToPerform: deploymentsToPerform From 202cb45e4e84d11425ceaf21703892b1708d9f4c Mon Sep 17 00:00:00 2001 From: AlexanderSehr Date: Sat, 28 Dec 2024 11:18:40 +0100 Subject: [PATCH 09/17] Set windows def --- constructs/managedDevOpsPool/deploymentFiles/sbx.pool.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constructs/managedDevOpsPool/deploymentFiles/sbx.pool.bicep b/constructs/managedDevOpsPool/deploymentFiles/sbx.pool.bicep index 6bc75286..2d1a2095 100644 --- a/constructs/managedDevOpsPool/deploymentFiles/sbx.pool.bicep +++ b/constructs/managedDevOpsPool/deploymentFiles/sbx.pool.bicep @@ -15,7 +15,7 @@ module managedDevOpsPoolDeployment '../templates/pool.deploy.bicep' = { params: { resourceLocation: resourceLocation computeGalleryName: 'alsehrcg' - computeGalleryImageDefinitionName: 'sid-linux' + computeGalleryImageDefinitionName: 'sid-windows' devCenterName: 'my-center' devCenterProjectName: 'my-project' organizationName: 'asehr' From 7945d739aab56f7837448af79f8f450e3d2a3457 Mon Sep 17 00:00:00 2001 From: AlexanderSehr Date: Sat, 28 Dec 2024 11:49:22 +0100 Subject: [PATCH 10/17] Updated docs --- ...ing images with the Azure Image Builder.md | 56 ++++++++++++++---- docs/wiki/media/image/imagetrigger.PNG | Bin 58992 -> 69494 bytes 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/docs/wiki/Creating images with the Azure Image Builder.md b/docs/wiki/Creating images with the Azure Image Builder.md index 40f01fe6..121e0d27 100644 --- a/docs/wiki/Creating images with the Azure Image Builder.md +++ b/docs/wiki/Creating images with the Azure Image Builder.md @@ -48,7 +48,7 @@ The image creation uses several components: > _**NOTE:**_ The construct was build with multiple environments and staging in mind. To this end, pipeline variable files contain one variable per suggested environment (for example `vmImage_sbx` & `vmImage_dev`) which is automatically referenced by the corresponding stage. For details on how to work with and configure these variables, please refer to this [section](./Staging). > -> For the rest of the documentation we will ignore these environments and just refer to the simple variable or parameter file to avoid confusion around which file we refer to. All concepts apply to all files, no matter the environment/stage. +> For the rest of the documentation we will ignore these environments and just refer to the simple variable or parameter/deployment file to avoid confusion around which file we refer to. All concepts apply to all files, no matter the environment/stage. ## File structure & flow @@ -70,7 +70,7 @@ This section gives you an overview of the solution's structure, that is, how its > **Note:** All files are written in a way that should make modifications easy. However, to help you get started, please take not of the following recommendations: > - If you want to add additional logic to your pipeline, make sure to modify the `pipeline.jobs.yml` template to ensure that the added / modified steps are applied equally across all stages. > - If you want to add additional resources to your deployment, make sure to modify the `image.deploy.bicep` file to ensure that the added / modified resources are applied to all environments equally. If you further want to ensure your template remains flexible, you can add additional parameters you can then reference from the `.image.bicep` file. -> - If you want to modify any of the existing module templates and discover a property you want to use is missing, you can simply add it to the corresponding module. However, to use it, make sure to not only add a parameter for your new property to the module, but also give it a value by providing it in the `image.deploy.bicep` file. +> - If you want to modify any of the existing module templates and discover a property you want to use is missing, you can simply add it to the corresponding module. However, to use it, make sure to not only add a parameter/deployment for your new property to the module, but also give it a value by providing it in the `image.deploy.bicep` file. # Process @@ -85,23 +85,23 @@ To prepare the construct for usage you have to perform two fundamental steps: For this step you have to update these files to your needs: - `.azuredevops\azureImageBuilder\variables.yml` -- `constructs\azureImageBuilder\deploymentFiles\.image.bicep` +- `constructs\azureImageBuilder\deploymentFiles\.image..bicep` ### Variables The first file, `variables.yml`, is a pipeline variable file. You should update at least the values: - `vmImage`: Set this to for example `ubuntu-latest` to leverage Microsoft-hosted agents. Leave it empty (`''`) if you use self-hosted agents. Do not remove it. - `poolName`: Set this to for example `myHostPool` to leverage your self-hosted agent pool. Leave it empty (`''`) if you use Microsoft-hosted agents. Do not remove it. - `serviceConnection`: This refers to your Azure DevOps service connection you use for your deployments. It should point into the subscription you want to deploy into. -- `deploymentMetadataLocation`: The location to store deployment metadata in. The location of the resources it specified directly in the deployment/parameter file. +- `deploymentMetadataLocation`: The location to store deployment metadata in. The location of the resources it specified directly in the deployment/parameter/deployment file. ### Parameters -Next, we have one deployment file, `.image.bicep` that hosts to the two phases in the deployment: Deploy all infrastructure components & build the image. +Next, we have one deployment file per OS, `.image..bicep` which contain the configuration for the infrastructure & image creation deployment. -The file comes with out-of-the box parameters that you can use aside from a few noteworthy exceptions: Update any name of a resource that is deployed and must be globally unique (for example storage accounts). +Each file comes with out-of-the box parameters that you can use aside from a few noteworthy exceptions: Update any name of a resource that is deployed and must be globally unique (for example storage accounts). -> **Note:** To keep the parameter files as simple as possible, all values that don't necessarily need you attention are hardcoded as default values in the corresponding template files. To get an overview about these 'defaults', you can simply navigate from the parameter file to the linked template. +> **Note:** To keep the parameter/deployment files as simple as possible, all values that don't necessarily need you attention are hardcoded as default values in the corresponding template files. To get an overview about these 'defaults', you can simply navigate from the parameter/deployment file to the linked template. -The parameter files are created with a Linux-Image in mind. However, they also contain examples on how the same implementation would look like for Windows images. Examples are always commented and can be used to replace the currently not commented values. +The parameter/deployment files are created with a Linux-Image in mind. However, they also contain examples on how the same implementation would look like for Windows images. Examples are always commented and can be used to replace the currently not commented values. As the deployments leverage [`AVM`](https://aka.ms/avm) modules you can find a full list of all supported parameters per module in that [repository's](https://www.github.com/Azure/bicep-registry-modules) modules. A valid example may be that you want to deploy the Image Template into a specific subnet for networking access. This and several other parameters are available and documented in the module's `readme.md`. @@ -131,7 +131,7 @@ For reference, in the provided Linux example we're looking at the following size The image template ultimately decides what happens during the image built. In this construct, it works in combination with the scripts provided in the `constructs\azureImageBuilder\scripts\uploads` folder. -When you eventually trigger the pipeline, it will upload any script in the `uploads` folder to a dedicated storage account for the image building process using a deployment script and then execute it as per the configured steps in the Image Template's parameter file's `customizationSteps` parameter. For Linux we use for example the following two steps: +When you eventually trigger the pipeline, it will upload any script in the `uploads` folder to a dedicated storage account for the image building process using a deployment script and then execute it as per the configured steps in the Image Template's parameter/deployment file's `customizationSteps` parameter. For Linux we use for example the following two steps: ```Bicep imageTemplateCustomizationSteps: [ @@ -154,6 +154,35 @@ imageTemplateCustomizationSteps: [ By default, it first installs PowerShell on the target machine and then continues by executing the installation script. Feel free to modify the existing script, or add new ones with new customization steps as you see fit. You can find a full list of all available steps [here](https://docs.microsoft.com/en-us/azure/virtual-machines/linux/image-builder-json#properties-customize). +> **NOTE:** If you don't want to build images for more than one OS you can remove the option from the pipelines by +> 1. Removing the corresponding runtime parameter/deployment from the `.azuredevops/azureImageBuilder/pipeline.yml` pipeline file +> ```yaml +> - name: osType +> displayName: OS to create the image for +> type: string +> default: Linux +> values: +> - Linux +> - Windows +> ``` +> 1. Removing each pass thru of the `osType` runtime parameter to the inner `pipeline.jobs.yml` pipeline file from the `.azuredevops/azureImageBuilder/pipeline.yml` pipeline file +> ```yaml +> osType: '${{ lower(parameters.osType) }}'` +> ``` +> 1. Removing the corresponding template parameter/deployment from the inner `.azuredevops/azureImageBuilder/.templates/pipeline.jobs.yml` pipeline file +> ```yaml +> - name: osType +> default: +> ``` +> 1. Removing each reference of the `osType` parameter/deployment from the code in the inner `.azuredevops/azureImageBuilder/.templates/pipeline.jobs.yml` pipeline file +> ```yaml +> # Change from +> Join-Path '$(deploymentFilesPath)' '${{ parameters.environment }}.image.${{ parameters. osType }}.bicep' +> # to +> Join-Path '$(deploymentFilesPath)' '${{ parameters.environment }}.image.bicep' +> ``` +> 1. Adjust your parameter/deployment files in `constructs/azureImageBuilder/deploymentFiles` to only contain names without the OS type embedded. For example, change `sbx.image.linux.bicep` to `sbx.image.bicep` +
@@ -199,14 +228,15 @@ So let's take a look at the different configuration options when running the pip | Runtime Parameter | Description | On first deployment | Additional notes | | - | - | - | - | | `Environment to start from` | The environment you want to start to deploy into as described [here](./Staging#3-run-the-pipeline) | Set to `SBX` | | -| `Scope of deployment` | Select whether you want to deploy all resources, all resources without triggering the image build, or only the image build | Set to deploy `All` or `Only base` resources | Overall you have the following options:

  • **`All`**: Deploys all resources end-to-end including an image build
  • **`Only removal`**: Only removes previous image templates (and their AIB resource groups) that match the provided Image Template name and are not in state `running`. Further, terminated deployment scripts who's name starts with the `defaultPrefix` specified in the `.image.bicep` file are removed. Is only executed if the `Pre-remove Image Template Resource Group` checkbox is selected too.
  • **`Only base`**: Deploys everything, but the image template. As such, no image is built
  • **`Only assets & image`**: Only uploads the latest installation files from the `uploads` folder and trigger an image build
  • **`Only image`**: Only trigger an image build
  • | +| `Scope of deployment` | Select whether you want to deploy all resources, all resources without triggering the image build, or only the image build | Set to deploy `All` or `Only base` resources | Overall you have the following options:

  • **`All`**: Deploys all resources end-to-end including an image build
  • **`Only removal`**: Only removes previous image templates (and their AIB resource groups) that match the provided Image Template name and are not in state `running`. Further, terminated deployment scripts who's name starts with the `defaultPrefix` specified in the `.image..bicep` file are removed. Is only executed if the `Pre-remove Image Template Resource Group` checkbox is selected too.
  • **`Only base`**: Deploys everything, but the image template. As such, no image is built
  • **`Only assets & image`**: Only uploads the latest installation files from the `uploads` folder and trigger an image build
  • **`Only image`**: Only trigger an image build
  • | +| `OS to create image for` | Enables you to select which OS to create the image for. Based on the selection, a different parameter/deployment file with set name is used. | Select OS you want to create the image for |
    • **`Linux`**: Will select a parameter/deployment file that follows the schema `.image.linux.bicep`
    • **`Windows`**: `.image.windows.bicep`
    | | `Wait for image build` | Specify whether to wait for the image build process during the pipeline run or not. The process itself is triggered asynchronously. | De-Select | You can use the 'Wait-ForImageBuild' script to check the status yourself (located at: `constructs\azureImageBuilder\scripts\image\Wait-ForImageBuild.ps1`).

    To execute it you will need the image template name (output value of the image template deployment) and the resource group the image template was deployed into. Is only considered, if the `Scope of the deployment` includes an image build. | -| `Pre-remove Image Template Resource Group` | Specify whether to remove previous image resources. This includes all Image Templates that match the naming schema defined in the parameter file - as long es their built is not in state `running`. | De-select | | +| `Pre-remove Image Template Resource Group` | Specify whether to remove previous image resources. This includes all Image Templates that match the naming schema defined in the parameter/deployment file - as long es their built is not in state `running`. | De-select | | ### First deployment When triggering the pipeline for the first time for any environment, make sure you either select `All` or `Only base` for the `Scope of the deployment`. In either case the pipeline will deploy all resources and scripts you will subsequently need to create the images. For any subsequent run, you can go with any option you need. -The steps the _Azure Image Builder_ performs on the image are defined by elements configured in the `customizationSteps` parameter of the image template parameter file. In our setup we reference one or multiple custom scripts that are uploaded by the template to a storage account ahead of the image deployment. +The steps the _Azure Image Builder_ performs on the image are defined by elements configured in the `customizationSteps` parameter of the image template parameter/deployment file. In our setup we reference one or multiple custom scripts that are uploaded by the template to a storage account ahead of the image deployment. The scripts are different for the type of OS and hence are also stored in two different folders in the `azureImageBuilder` construct: - Linux: `constructs\azureImageBuilder\scripts\uploads\linux` @@ -235,7 +265,7 @@ Usually, when you will operate the pipeline you would want to either run in scop

    Stacked images - Any time you run an image built you first have to decide whether you want to build an image from the ground up (using e.g. a marketplace image as the basis) or build on an existing custom image. In either case you have to configure the image template parameter file in question with regards to the `imageSource` parameter. + Any time you run an image built you first have to decide whether you want to build an image from the ground up (using e.g. a marketplace image as the basis) or build on an existing custom image. In either case you have to configure the image template parameter/deployment file in question with regards to the `imageSource` parameter. To reference a marketplace image use the syntax: ```Bicep { diff --git a/docs/wiki/media/image/imagetrigger.PNG b/docs/wiki/media/image/imagetrigger.PNG index 7998e8ac8271392ad97cc0c2eb9ff33d4ebf60d1..174e9436a9443d7965b146beb54eb9fe24b3c91b 100644 GIT binary patch delta 35960 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 PQ^=iHJePX*=Dq&`4nZr1 delta 25375 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 pK}~xEkN>72;QpW6zoK@jt$6m`&{{Azf Date: Sat, 28 Dec 2024 22:20:26 +0100 Subject: [PATCH 11/17] Update to latest --- .../scripts/uploads/windows/Initialize-WindowsSoftware.ps1 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/constructs/azureImageBuilder/scripts/uploads/windows/Initialize-WindowsSoftware.ps1 b/constructs/azureImageBuilder/scripts/uploads/windows/Initialize-WindowsSoftware.ps1 index e20ee63f..e8d515d7 100644 --- a/constructs/azureImageBuilder/scripts/uploads/windows/Initialize-WindowsSoftware.ps1 +++ b/constructs/azureImageBuilder/scripts/uploads/windows/Initialize-WindowsSoftware.ps1 @@ -634,6 +634,13 @@ LogInfo('Install azure cli start') $null = choco install azure-cli -y -v LogInfo('Install azure cli end') +########################### +## Install BICEP CLI ## +########################### +LogInfo('Install BICEP start') +choco install bicep +LogInfo('Install BICEP end') + ############################### ## Install Extensions CLI # ############################### From 02e6021cf0ae237335862a754743206dece8b0f5 Mon Sep 17 00:00:00 2001 From: AlexanderSehr Date: Sat, 28 Dec 2024 22:22:53 +0100 Subject: [PATCH 12/17] Update to latest --- .../azureImageBuilder/deploymentFiles/sbx.image.linux.bicep | 1 - 1 file changed, 1 deletion(-) diff --git a/constructs/azureImageBuilder/deploymentFiles/sbx.image.linux.bicep b/constructs/azureImageBuilder/deploymentFiles/sbx.image.linux.bicep index c58b266d..9760cc5e 100644 --- a/constructs/azureImageBuilder/deploymentFiles/sbx.image.linux.bicep +++ b/constructs/azureImageBuilder/deploymentFiles/sbx.image.linux.bicep @@ -62,7 +62,6 @@ module imageDeployment '../templates/image.deploy.bicep' = { value: loadTextContent('../scripts/uploads/linux/${initializeSoftwareScriptName}') } ] - // Linux Example imageTemplateImageSource: { type: 'PlatformImage' publisher: 'canonical' From d8315b84d26f5e0026080103cb8ce03017fc85a6 Mon Sep 17 00:00:00 2001 From: AlexanderSehr Date: Sat, 28 Dec 2024 22:26:37 +0100 Subject: [PATCH 13/17] Update to latest --- .../azureImageBuilder/deploymentFiles/sbx.image.windows.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep b/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep index 38113484..cc6ec536 100644 --- a/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep +++ b/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep @@ -70,7 +70,7 @@ module imageDeployment '../templates/image.deploy.bicep' = { version: 'latest' } - imageTemplateResourceGroupName: '' + imageTemplateResourceGroupName: '' // Setting to empty as a custom staging resource group currently fails the creation of a windows image for an unknown reason imageTemplateCustomizationSteps: [ { type: 'PowerShell' From 6acc79f73677f5f4f88860f905227eb676ef627e Mon Sep 17 00:00:00 2001 From: AlexanderSehr Date: Sat, 28 Dec 2024 22:30:46 +0100 Subject: [PATCH 14/17] Update to latest --- README.md | 4 ++-- constructs/azureImageBuilder/README.md | 4 ++-- constructs/managedDevOpsPool/README.md | 4 ++-- ...eating images with the Azure Image Builder.md | 16 ++++++++-------- docs/wiki/Self-hosted Managed DevOps Pool.md | 6 +++--- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 1e7ac458..5cff5e78 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,8 @@ Currently the repository contains the following constructs: | Construct | Description | Path | | - | - | - | -| Azure Image Builder | This construct provides a packages solution to create custom images using the Azure Image Builder service with an Azure Compute Gallery, including guidelines to operate it. | [`constructs\azureImageBuilder`](https://github.com/Azure/DevOps-Self-Hosted/tree/main/constructs/azureImageBuilder) | -| Managed DevOps Pool | This construct provides a packaged solution to deploy the infrastructure for self-hosted Managed DevOps Pools, including guidelines to maintain it. | [`constructs\managedDevOpsPool`](https://github.com/Azure/DevOps-Self-Hosted/tree/main/constructs/managedDevOpsPool) | +| Azure Image Builder | This construct provides a packages solution to create custom images using the Azure Image Builder service with an Azure Compute Gallery, including guidelines to operate it. | [`constructs/azureImageBuilder`](https://github.com/Azure/DevOps-Self-Hosted/tree/main/constructs/azureImageBuilder) | +| Managed DevOps Pool | This construct provides a packaged solution to deploy the infrastructure for self-hosted Managed DevOps Pools, including guidelines to maintain it. | [`constructs/managedDevOpsPool`](https://github.com/Azure/DevOps-Self-Hosted/tree/main/constructs/managedDevOpsPool) | For detailed information on the constructs and how to get started, please continue to the [Wiki](https://github.com/Azure/DevOps-Self-Hosted/wiki). diff --git a/constructs/azureImageBuilder/README.md b/constructs/azureImageBuilder/README.md index 467b2b17..87fbbdb4 100644 --- a/constructs/azureImageBuilder/README.md +++ b/constructs/azureImageBuilder/README.md @@ -2,7 +2,7 @@ This construct consists of several components in different places. You can use the following table to make sure you are aware of all the ones you need: -- Construct Templates, Scripts & Deployment Files: Folder `constructs\azureImageBuilder` -- Azure DevOps Pipelines: Folder `.azuredevops\azureImageBuilder` +- Construct Templates, Scripts & Deployment Files: Folder `constructs/azureImageBuilder` +- Azure DevOps Pipelines: Folder `.azuredevops/azureImageBuilder` For further information, please refer to the Wiki for information on how to apply the construct. diff --git a/constructs/managedDevOpsPool/README.md b/constructs/managedDevOpsPool/README.md index c1dab141..4cd10b45 100644 --- a/constructs/managedDevOpsPool/README.md +++ b/constructs/managedDevOpsPool/README.md @@ -2,7 +2,7 @@ This construct consists of several components in different places. You can use the following table to make sure you are aware of all the ones you need: -- Construct Templates, Scripts & Deployment Files: Folder `constructs\managedDevOpsPool` -- Azure DevOps Pipelines: Folder `.azuredevops\managedDevOpsPool` +- Construct Templates, Scripts & Deployment Files: Folder `constructs/managedDevOpsPool` +- Azure DevOps Pipelines: Folder `.azuredevops/managedDevOpsPool` For further information, please refer to the Wiki for information on how to apply the construct. diff --git a/docs/wiki/Creating images with the Azure Image Builder.md b/docs/wiki/Creating images with the Azure Image Builder.md index 121e0d27..47a06114 100644 --- a/docs/wiki/Creating images with the Azure Image Builder.md +++ b/docs/wiki/Creating images with the Azure Image Builder.md @@ -84,8 +84,8 @@ To prepare the construct for usage you have to perform two fundamental steps:

    1. Configure the deployment parameters For this step you have to update these files to your needs: -- `.azuredevops\azureImageBuilder\variables.yml` -- `constructs\azureImageBuilder\deploymentFiles\.image..bicep` +- `.azuredevops/azureImageBuilder/variables.yml` +- `constructs/azureImageBuilder/deploymentFiles/.image..bicep` ### Variables The first file, `variables.yml`, is a pipeline variable file. You should update at least the values: @@ -129,7 +129,7 @@ For reference, in the provided Linux example we're looking at the following size #### Special case: Resource **Image Template** -The image template ultimately decides what happens during the image built. In this construct, it works in combination with the scripts provided in the `constructs\azureImageBuilder\scripts\uploads` folder. +The image template ultimately decides what happens during the image built. In this construct, it works in combination with the scripts provided in the `constructs/azureImageBuilder/scripts/uploads` folder. When you eventually trigger the pipeline, it will upload any script in the `uploads` folder to a dedicated storage account for the image building process using a deployment script and then execute it as per the configured steps in the Image Template's parameter/deployment file's `customizationSteps` parameter. For Linux we use for example the following two steps: @@ -219,7 +219,7 @@ To do so, you have to perform the following steps: ## Deployment -The creation of the image alongside its resources is handled by the `.azuredevops\azureImageBuilder\pipeline.yml` pipeline. Given the proper configuration, it creates all required resources in the designated environment and optionally triggers the image creation right after. +The creation of the image alongside its resources is handled by the `.azuredevops/azureImageBuilder/pipeline.yml` pipeline. Given the proper configuration, it creates all required resources in the designated environment and optionally triggers the image creation right after. Run workflow @@ -230,7 +230,7 @@ So let's take a look at the different configuration options when running the pip | `Environment to start from` | The environment you want to start to deploy into as described [here](./Staging#3-run-the-pipeline) | Set to `SBX` | | | `Scope of deployment` | Select whether you want to deploy all resources, all resources without triggering the image build, or only the image build | Set to deploy `All` or `Only base` resources | Overall you have the following options:

  • **`All`**: Deploys all resources end-to-end including an image build
  • **`Only removal`**: Only removes previous image templates (and their AIB resource groups) that match the provided Image Template name and are not in state `running`. Further, terminated deployment scripts who's name starts with the `defaultPrefix` specified in the `.image..bicep` file are removed. Is only executed if the `Pre-remove Image Template Resource Group` checkbox is selected too.
  • **`Only base`**: Deploys everything, but the image template. As such, no image is built
  • **`Only assets & image`**: Only uploads the latest installation files from the `uploads` folder and trigger an image build
  • **`Only image`**: Only trigger an image build
  • | | `OS to create image for` | Enables you to select which OS to create the image for. Based on the selection, a different parameter/deployment file with set name is used. | Select OS you want to create the image for |
    • **`Linux`**: Will select a parameter/deployment file that follows the schema `.image.linux.bicep`
    • **`Windows`**: `.image.windows.bicep`
    | -| `Wait for image build` | Specify whether to wait for the image build process during the pipeline run or not. The process itself is triggered asynchronously. | De-Select | You can use the 'Wait-ForImageBuild' script to check the status yourself (located at: `constructs\azureImageBuilder\scripts\image\Wait-ForImageBuild.ps1`).

    To execute it you will need the image template name (output value of the image template deployment) and the resource group the image template was deployed into. Is only considered, if the `Scope of the deployment` includes an image build. | +| `Wait for image build` | Specify whether to wait for the image build process during the pipeline run or not. The process itself is triggered asynchronously. | De-Select | You can use the 'Wait-ForImageBuild' script to check the status yourself (located at: `constructs/azureImageBuilder/scripts/image/Wait-ForImageBuild.ps1`).

    To execute it you will need the image template name (output value of the image template deployment) and the resource group the image template was deployed into. Is only considered, if the `Scope of the deployment` includes an image build. | | `Pre-remove Image Template Resource Group` | Specify whether to remove previous image resources. This includes all Image Templates that match the naming schema defined in the parameter/deployment file - as long es their built is not in state `running`. | De-select | | ### First deployment @@ -239,8 +239,8 @@ When triggering the pipeline for the first time for any environment, make sure y The steps the _Azure Image Builder_ performs on the image are defined by elements configured in the `customizationSteps` parameter of the image template parameter/deployment file. In our setup we reference one or multiple custom scripts that are uploaded by the template to a storage account ahead of the image deployment. The scripts are different for the type of OS and hence are also stored in two different folders in the `azureImageBuilder` construct: -- Linux: `constructs\azureImageBuilder\scripts\uploads\linux` -- Windows: `constructs\azureImageBuilder\scripts\uploads\windows` +- Linux: `constructs/azureImageBuilder/scripts/uploads/linux` +- Windows: `constructs/azureImageBuilder/scripts/uploads/windows` One of the main tasks performed by these scripts are the installation of the baseline modules and software we want to have installed on the image. Prime candidates are for example the Az PowerShell modules, Bicep CLI and Terraform CLI. @@ -291,7 +291,7 @@ Usually, when you will operate the pipeline you would want to either run in scop # Out of the box installed components -Following you can find an overview of the installed elements currently implemented in the scripts of the `constructs\azureImageBuilder\scripts\uploads` folder: +Following you can find an overview of the installed elements currently implemented in the scripts of the `constructs/azureImageBuilder/scripts/uploads` folder: | OS | | | Windows | Linux | | - | - | - | - | - | diff --git a/docs/wiki/Self-hosted Managed DevOps Pool.md b/docs/wiki/Self-hosted Managed DevOps Pool.md index e3e512fe..a873a8cc 100644 --- a/docs/wiki/Self-hosted Managed DevOps Pool.md +++ b/docs/wiki/Self-hosted Managed DevOps Pool.md @@ -90,8 +90,8 @@ To prepare the construct for deployment you have to perform two fundamental step For this step you have to update these files to your needs: -- `.azuredevops\managedDevOpsPool\variables.yml` -- `constructs\managedDevOpsPool\deploymentFiles\pool.bicep` +- `.azuredevops/managedDevOpsPool/variables.yml` +- `constructs/managedDevOpsPool/deploymentFiles/pool.bicep` ### Variables @@ -225,7 +225,7 @@ To do so, you have to perform the following steps: ## Deployment -The creation of the Managed DevOps Pool alongside its resources is handled by the `.azuredevops\managedDevOpsPool\pipeline.yml` pipeline. Given a proper configuration, it creates all required resources in the designated environment, including the agent pool's registration in Azure DevOps. +The creation of the Managed DevOps Pool alongside its resources is handled by the `.azuredevops/managedDevOpsPool/pipeline.yml` pipeline. Given a proper configuration, it creates all required resources in the designated environment, including the agent pool's registration in Azure DevOps. Also, when triggering the pipeline you have several configuration options to chose from: From d876268e3753a9e64e98760688c4e876e560333a Mon Sep 17 00:00:00 2001 From: AlexanderSehr Date: Sat, 28 Dec 2024 22:33:55 +0100 Subject: [PATCH 15/17] Update to latest --- .../azureImageBuilder/deploymentFiles/sbx.image.windows.bicep | 3 +++ 1 file changed, 3 insertions(+) diff --git a/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep b/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep index cc6ec536..f9a1d996 100644 --- a/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep +++ b/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep @@ -68,6 +68,9 @@ module imageDeployment '../templates/image.deploy.bicep' = { offer: 'Windows-11' sku: 'win11-24h2-avd' version: 'latest' + // Custom image example + // type: 'SharedImageVersion' + // imageVersionID: '${subscription().id}/resourceGroups/myRg/providers/Microsoft.Compute/galleries/alsehrcg/images/${computeGalleryImageDefinitionName}/versions/0.24470.675' } imageTemplateResourceGroupName: '' // Setting to empty as a custom staging resource group currently fails the creation of a windows image for an unknown reason From baa5216623044d3cb5374e1bce7c92c65b4dae77 Mon Sep 17 00:00:00 2001 From: AlexanderSehr Date: Sun, 29 Dec 2024 08:48:29 +0100 Subject: [PATCH 16/17] Undid env-specific changes --- .azuredevops/azureImageBuilder/variables.yml | 18 +++++++++--------- .azuredevops/managedDevOpsPool/variables.yml | 18 +++++++++--------- .../deploymentFiles/sbx.image.linux.bicep | 6 +++--- .../deploymentFiles/sbx.image.windows.bicep | 6 +++--- .../deploymentFiles/sbx.pool.bicep | 12 ++++++------ ...ting images with the Azure Image Builder.md | 4 ++-- 6 files changed, 32 insertions(+), 32 deletions(-) diff --git a/.azuredevops/azureImageBuilder/variables.yml b/.azuredevops/azureImageBuilder/variables.yml index 04a0e9f9..4d380202 100644 --- a/.azuredevops/azureImageBuilder/variables.yml +++ b/.azuredevops/azureImageBuilder/variables.yml @@ -12,17 +12,17 @@ variables: ## GENERAL ## ############# #region shared - vmImage_sbx: '' # 'ubuntu-latest' # Use this for microsoft-hosted agents - vmImage_dev: '' # 'ubuntu-latest' # Use this for microsoft-hosted agents - vmImage_prd: '' # 'ubuntu-latest' # Use this for microsoft-hosted agents + vmImage_sbx: 'ubuntu-latest' # Use this for microsoft-hosted agents + vmImage_dev: 'ubuntu-latest' # Use this for microsoft-hosted agents + vmImage_prd: 'ubuntu-latest' # Use this for microsoft-hosted agents - poolName_sbx: 'core-vmss' # Use this for self-hosted agents - poolName_dev: 'core-vmss' # Use this for self-hosted agents - poolName_prd: 'core-vmss' # Use this for self-hosted agents + poolName_sbx: '' # Use this for self-hosted agents + poolName_dev: '' # Use this for self-hosted agents + poolName_prd: '' # Use this for self-hosted agents - serviceConnection_sbx: 'OIDC-Private' - serviceConnection_dev: 'OIDC-Private' - serviceConnection_prd: 'OIDC-Private' + serviceConnection_sbx: '' + serviceConnection_dev: '' + serviceConnection_prd: '' #endregion #region specific diff --git a/.azuredevops/managedDevOpsPool/variables.yml b/.azuredevops/managedDevOpsPool/variables.yml index b5a27e87..6e594465 100644 --- a/.azuredevops/managedDevOpsPool/variables.yml +++ b/.azuredevops/managedDevOpsPool/variables.yml @@ -12,17 +12,17 @@ variables: ## GENERAL ## ############# #region shared - vmImage_sbx: '' # 'ubuntu-latest' # Use this for microsoft-hosted agents - vmImage_dev: '' # 'ubuntu-latest' # Use this for microsoft-hosted agents - vmImage_prd: '' # 'ubuntu-latest' # Use this for microsoft-hosted agents + vmImage_sbx: 'ubuntu-latest' # Use this for microsoft-hosted agents + vmImage_dev: 'ubuntu-latest' # Use this for microsoft-hosted agents + vmImage_prd: 'ubuntu-latest' # Use this for microsoft-hosted agents - poolName_sbx: 'core-vmss' # Use this for self-hosted agents - poolName_dev: 'core-vmss' # Use this for self-hosted agents - poolName_prd: 'core-vmss' # Use this for self-hosted agents + poolName_sbx: '' # Use this for self-hosted agents + poolName_dev: '' # Use this for self-hosted agents + poolName_prd: '' # Use this for self-hosted agents - serviceConnection_sbx: 'OIDC-Private' - serviceConnection_dev: 'OIDC-Private' - serviceConnection_prd: 'OIDC-Private' + serviceConnection_sbx: '' + serviceConnection_dev: '' + serviceConnection_prd: '' #endregion #region specific diff --git a/constructs/azureImageBuilder/deploymentFiles/sbx.image.linux.bicep b/constructs/azureImageBuilder/deploymentFiles/sbx.image.linux.bicep index 9760cc5e..57b83db6 100644 --- a/constructs/azureImageBuilder/deploymentFiles/sbx.image.linux.bicep +++ b/constructs/azureImageBuilder/deploymentFiles/sbx.image.linux.bicep @@ -22,7 +22,7 @@ param waitForImageBuild bool = true // Template Deployment // ///////////////////////////// var computeGalleryImageDefinitionName = 'sid-linux' -var assetsStorageAccountName = 'alsehrst' +var assetsStorageAccountName = '' var assetsStorageAccountContainerName = 'aibscripts' var installPwshScriptName = 'Install-LinuxPowerShell.sh' var initializeSoftwareScriptName = 'Initialize-LinuxSoftware.ps1' @@ -32,7 +32,7 @@ module imageDeployment '../templates/image.deploy.bicep' = { params: { resourceLocation: resourceLocation deploymentsToPerform: deploymentsToPerform - computeGalleryName: 'alsehrcg' + computeGalleryName: '' computeGalleryImageDefinitionName: computeGalleryImageDefinitionName waitForImageBuild: waitForImageBuild computeGalleryImageDefinitions: [ @@ -70,7 +70,7 @@ module imageDeployment '../templates/image.deploy.bicep' = { version: 'latest' // Custom image example // type: 'SharedImageVersion' - // imageVersionID: '${subscription().id}/resourceGroups/myRg/providers/Microsoft.Compute/galleries/alsehrcg/images/${computeGalleryImageDefinitionName}/versions/0.24470.675' + // imageVersionID: '${subscription().id}/resourceGroups/myRg/providers/Microsoft.Compute/galleries//images/${computeGalleryImageDefinitionName}/versions/0.24470.675' } imageTemplateCustomizationSteps: [ { diff --git a/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep b/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep index f9a1d996..0b86d148 100644 --- a/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep +++ b/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep @@ -22,7 +22,7 @@ param waitForImageBuild bool = true // Template Deployment // ///////////////////////////// var computeGalleryImageDefinitionName = 'sid-windows' -var assetsStorageAccountName = 'alsehrst' +var assetsStorageAccountName = '' var assetsStorageAccountContainerName = 'aibscripts' var installPwshScriptName = 'Install-WindowsPowerShell.ps1' var initializeSoftwareScriptName = 'Initialize-WindowsSoftware.ps1' @@ -32,7 +32,7 @@ module imageDeployment '../templates/image.deploy.bicep' = { params: { resourceLocation: resourceLocation deploymentsToPerform: deploymentsToPerform - computeGalleryName: 'alsehrcg' + computeGalleryName: '' computeGalleryImageDefinitionName: computeGalleryImageDefinitionName waitForImageBuild: waitForImageBuild @@ -70,7 +70,7 @@ module imageDeployment '../templates/image.deploy.bicep' = { version: 'latest' // Custom image example // type: 'SharedImageVersion' - // imageVersionID: '${subscription().id}/resourceGroups/myRg/providers/Microsoft.Compute/galleries/alsehrcg/images/${computeGalleryImageDefinitionName}/versions/0.24470.675' + // imageVersionID: '${subscription().id}/resourceGroups/myRg/providers/Microsoft.Compute/galleries//images/${computeGalleryImageDefinitionName}/versions/0.24470.675' } imageTemplateResourceGroupName: '' // Setting to empty as a custom staging resource group currently fails the creation of a windows image for an unknown reason diff --git a/constructs/managedDevOpsPool/deploymentFiles/sbx.pool.bicep b/constructs/managedDevOpsPool/deploymentFiles/sbx.pool.bicep index 2d1a2095..29f8b764 100644 --- a/constructs/managedDevOpsPool/deploymentFiles/sbx.pool.bicep +++ b/constructs/managedDevOpsPool/deploymentFiles/sbx.pool.bicep @@ -14,16 +14,16 @@ module managedDevOpsPoolDeployment '../templates/pool.deploy.bicep' = { name: '${uniqueString(deployment().name, resourceLocation)}-managedPool-sbx' params: { resourceLocation: resourceLocation - computeGalleryName: 'alsehrcg' - computeGalleryImageDefinitionName: 'sid-windows' + computeGalleryName: '' + computeGalleryImageDefinitionName: 'sid-linux' devCenterName: 'my-center' devCenterProjectName: 'my-project' - organizationName: 'asehr' - projectNames: ['Onyx'] - poolName: 'onyx-pool' + organizationName: '' + projectNames: [''] + poolName: '' poolMaximumConcurrency: 5 // Tenant-specific 'DevOpsInfrastructure' Enterprise Application objectId. // Can be fetched by running `(Get-AzAdServicePrincipal -DisplayName 'DevOpsInfrastructure').Id` while logged into the tenant to deploy into. - devOpsInfrastructureEnterpriseApplicationObjectId: 'a67e26cd-08dc-47be-8217-df02edb89ba8' + devOpsInfrastructureEnterpriseApplicationObjectId: '' } } diff --git a/docs/wiki/Creating images with the Azure Image Builder.md b/docs/wiki/Creating images with the Azure Image Builder.md index 47a06114..14359c25 100644 --- a/docs/wiki/Creating images with the Azure Image Builder.md +++ b/docs/wiki/Creating images with the Azure Image Builder.md @@ -138,13 +138,13 @@ imageTemplateCustomizationSteps: [ { type: 'Shell' name: 'PowerShell installation' - scriptUri: 'https://alsehrst.blob.core.windows.net/aibscripts/Install-LinuxPowerShell.sh' + scriptUri: 'https://.blob.core.windows.net/aibscripts/Install-LinuxPowerShell.sh' } { type: 'Shell' name: 'Prepare software installation' inline: [ - 'wget \'https://alsehrst.blob.core.windows.net/aibscripts/Initialize-LinuxSoftware.ps1\' -O \'Initialize-LinuxSoftware.ps1\'' + 'wget \'https://.blob.core.windows.net/aibscripts/Initialize-LinuxSoftware.ps1\' -O \'Initialize-LinuxSoftware.ps1\'' 'sed -i \'s/\r$//' 'Initialize-LinuxSoftware.ps1\'' 'pwsh \'Initialize-LinuxSoftware.ps1\'' ] From 78718e71e59950566371aebfe280f6e6c86d74bf Mon Sep 17 00:00:00 2001 From: AlexanderSehr Date: Sun, 29 Dec 2024 08:50:29 +0100 Subject: [PATCH 17/17] Update to latest --- .../deploymentFiles/sbx.image.linux.bicep | 9 ++++++--- .../deploymentFiles/sbx.image.windows.bicep | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/constructs/azureImageBuilder/deploymentFiles/sbx.image.linux.bicep b/constructs/azureImageBuilder/deploymentFiles/sbx.image.linux.bicep index 57b83db6..5ae03116 100644 --- a/constructs/azureImageBuilder/deploymentFiles/sbx.image.linux.bicep +++ b/constructs/azureImageBuilder/deploymentFiles/sbx.image.linux.bicep @@ -18,15 +18,18 @@ param resourceLocation string = 'NorthEurope' @description('Optional. A parameter to control if the deployment should wait for the image build to complete.') param waitForImageBuild bool = true -///////////////////////////// -// Template Deployment // -///////////////////////////// +/////////////////////////////////////////////// +// Multi-referenced deployment variables // +/////////////////////////////////////////////// var computeGalleryImageDefinitionName = 'sid-linux' var assetsStorageAccountName = '' var assetsStorageAccountContainerName = 'aibscripts' var installPwshScriptName = 'Install-LinuxPowerShell.sh' var initializeSoftwareScriptName = 'Initialize-LinuxSoftware.ps1' +///////////////////////////// +// Template deployment // +///////////////////////////// module imageDeployment '../templates/image.deploy.bicep' = { name: '${uniqueString(deployment().name, resourceLocation)}-image-sbx' params: { diff --git a/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep b/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep index 0b86d148..0a8da58d 100644 --- a/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep +++ b/constructs/azureImageBuilder/deploymentFiles/sbx.image.windows.bicep @@ -18,15 +18,18 @@ param resourceLocation string = 'NorthEurope' @description('Optional. A parameter to control if the deployment should wait for the image build to complete.') param waitForImageBuild bool = true -///////////////////////////// -// Template Deployment // -///////////////////////////// +/////////////////////////////////////////////// +// Multi-referenced deployment variables // +/////////////////////////////////////////////// var computeGalleryImageDefinitionName = 'sid-windows' var assetsStorageAccountName = '' var assetsStorageAccountContainerName = 'aibscripts' var installPwshScriptName = 'Install-WindowsPowerShell.ps1' var initializeSoftwareScriptName = 'Initialize-WindowsSoftware.ps1' +///////////////////////////// +// Template deployment // +///////////////////////////// module imageDeployment '../templates/image.deploy.bicep' = { name: '${uniqueString(deployment().name, resourceLocation)}-image-sbx' params: {