diff --git a/doc/how-to-build-customized-image.md b/doc/how-to-build-customized-image.md new file mode 100644 index 00000000..2f1f1da7 --- /dev/null +++ b/doc/how-to-build-customized-image.md @@ -0,0 +1,76 @@ +# Quick guide for the customized image + +This is the quick guide about how to geneate the customized image for Dev Box. +The main steps are as below: +- Create a Gallery +- Configure for Gallery +- Create Service Principal +- Generate the image + +## Create a Gallery +1. Search "Azure Compute Galleries" in the Azure Portal, click the service "Azure compute galleries" +![Search Gallery](/doc/media/search-azure-compute-gallery.png) + +2. Click the "Create" button +![Create Gallery](/doc/media/create-azure-compute-gallery.png) + +3. Give the gallery name and select the resource group, then click "Review + Create" +![Create Gallery Detail](/doc/media/create-azure-compute-gallery-detail.png) + +## Configure for Gallery +1. Fork the base repository [Azure/dev-box-images](https://github.com/Azure/dev-box-images) +![Fork Base repo](/doc/media/fork-base-repo.png) + +2. In your forked repository, change the value in gallery.yml: + +name: "the gallery name that we just created in the above step" +resourceGroup: "the resource group that the gallery is located" + +Please check the mapping as below: +![Update Gallery Yml](/doc/media/update-gallery-yml.png) + +## Create Service Principal + +1. Create a Service Principal with the Azure CLI command as below: + +az ad sp create-for-rbac --sdk-auth --role contributor --scopes /subscriptions/ -n + +You will get the output as below: +{ + "clientId": "", + "clientSecret": "", + "subscriptionId": "", + "tenantId": "" + (...) +} + +Remove all the line breaks and keep a single link as below: +{ "clientId": "", "clientSecret": "", "subscriptionId": "", "tenantId": "", (...) } + +2. Go to the settings of your forked GitHub project +![GitHub Repo Setting](/doc/media/github-repo-settings.png) + +3. Click "Secrets" -> "Actions" -> "New Repository Secret" +![New Repository Secret](/doc/media/new-repo-secret.png) + +Secret name is "AZURE_CREDENTIALS". Value is the single line that we prepared before as below: +{ "clientId": "", "clientSecret": "", "subscriptionId": "", "tenantId": "", (...) } + +## Generate the image +1. Go to the forked GitHub project’s "Actions" page +![GitHub Actions](/doc/media/github-actions.png) + +Enable it if there is an option asking to click. + +2. Define the software to install +Go to images/VSCodeBox/build.pkr.hcl +If the software can be installed by choco, just execute the command "choco install" to install. +Note: --confirm is required otherwise, it will be installed by confirm yes or no. +If the software cannot be installed by choco, please write up the related PowerShell script to install the specific software. + +![Customize Software](/doc/media/cutomize-software.png) + +Once you commit the change under the folder images or scripts, the GitHub action pipeline will be triggered. + +3. Go to the GitHub Actions, you will see the status of the pipeline +![GitHub Actions Workflow](/doc/media/github-actions-workflow.png) diff --git a/doc/media/create-azure-compute-gallery-detail.png b/doc/media/create-azure-compute-gallery-detail.png new file mode 100644 index 00000000..635d4b2e Binary files /dev/null and b/doc/media/create-azure-compute-gallery-detail.png differ diff --git a/doc/media/create-azure-compute-gallery.png b/doc/media/create-azure-compute-gallery.png new file mode 100644 index 00000000..a46bb76f Binary files /dev/null and b/doc/media/create-azure-compute-gallery.png differ diff --git a/doc/media/cutomize-software.png b/doc/media/cutomize-software.png new file mode 100644 index 00000000..1085642d Binary files /dev/null and b/doc/media/cutomize-software.png differ diff --git a/doc/media/fork-base-repo.png b/doc/media/fork-base-repo.png new file mode 100644 index 00000000..ed968734 Binary files /dev/null and b/doc/media/fork-base-repo.png differ diff --git a/doc/media/github-actions-workflow.png b/doc/media/github-actions-workflow.png new file mode 100644 index 00000000..9d10f07f Binary files /dev/null and b/doc/media/github-actions-workflow.png differ diff --git a/doc/media/github-actions.png b/doc/media/github-actions.png new file mode 100644 index 00000000..db29b6de Binary files /dev/null and b/doc/media/github-actions.png differ diff --git a/doc/media/github-repo-settings.png b/doc/media/github-repo-settings.png new file mode 100644 index 00000000..b5efd10f Binary files /dev/null and b/doc/media/github-repo-settings.png differ diff --git a/doc/media/new-repo-secret.png b/doc/media/new-repo-secret.png new file mode 100644 index 00000000..9356a64a Binary files /dev/null and b/doc/media/new-repo-secret.png differ diff --git a/doc/media/search-azure-compute-gallery.png b/doc/media/search-azure-compute-gallery.png new file mode 100644 index 00000000..48fd9994 Binary files /dev/null and b/doc/media/search-azure-compute-gallery.png differ diff --git a/doc/media/update-gallery-yml.png b/doc/media/update-gallery-yml.png new file mode 100644 index 00000000..5fa443eb Binary files /dev/null and b/doc/media/update-gallery-yml.png differ diff --git a/images/JavaEclipseBox/build.pkr.hcl b/images/JavaEclipseBox/build.pkr.hcl new file mode 100644 index 00000000..d0a7c4f0 --- /dev/null +++ b/images/JavaEclipseBox/build.pkr.hcl @@ -0,0 +1,122 @@ +packer { + required_plugins { + # https://github.com/rgl/packer-plugin-windows-update + windows-update = { + version = "0.14.1" + source = "github.com/rgl/windows-update" + } + } +} + +# https://www.packer.io/plugins/builders/azure/arm +source "azure-arm" "vm" { + azure_tags = { + branch = var.branch + build = timestamp() + commit = var.commit + } + communicator = "winrm" + winrm_username = "packer" + winrm_insecure = true + winrm_use_ssl = true + image_publisher = "microsoftwindowsdesktop" + image_offer = "windows-ent-cpc" + image_sku = "win11-21h2-ent-cpc-m365" + image_version = "latest" + use_azure_cli_auth = true + managed_image_name = var.name + managed_image_resource_group_name = var.galleryResourceGroup + location = var.location + temp_resource_group_name = var.tempResourceGroup + build_resource_group_name = var.buildResourceGroup + user_assigned_managed_identities = var.identities + async_resourcegroup_delete = true + os_type = "Windows" + vm_size = "Standard_D8s_v3" + shared_image_gallery_destination { + subscription = var.subscription + resource_group = var.galleryResourceGroup + gallery_name = var.galleryName + image_name = var.name + image_version = var.version + replication_regions = var.replicaLocations + storage_account_type = "Standard_LRS" + } +} + +build { + sources = ["source.azure-arm.vm"] + + provisioner "powershell" { + environment_vars = [ + "ADMIN_USERNAME=${build.User}", + "ADMIN_PASSWORD=${build.Password}" + ] + script = "../../scripts/Enable-AutoLogon.ps1" + } + + provisioner "windows-restart" { + # needed to get elevated script execution working + restart_timeout = "30m" + pause_before = "2m" + } + + # https://github.com/rgl/packer-plugin-windows-update + provisioner "windows-update" { + } + + provisioner "powershell" { + elevated_user = build.User + elevated_password = build.Password + scripts = [ + "../../scripts/Install-PsModules.ps1", + "../../scripts/Install-AzPsModule.ps1", + "../../scripts/Install-Chocolatey.ps1" + ] + } + + provisioner "powershell" { + elevated_user = build.User + elevated_password = build.Password + inline = [ + "choco install git --yes --no-progres", + "choco install openjdk11 --yes --no-progres", + "choco install maven --version=3.8.4 --yes --no-progres", + "choco install postman --yes --no-progres", + "choco install googlechrome --yes --no-progres" + ] + } + + provisioner "powershell" { + elevated_user = build.User + elevated_password = build.Password + scripts = [ + "../../scripts/Install-HyperV.ps1", + "../../scripts/Install-Python3.8.ps1", + "../../scripts/Install-AzureCLI.ps1", + "../../scripts/Install-VSCode.ps1", + "../../scripts/Install-Eclipse.ps1", + "../../scripts/Install-GCloudCLI.ps1" # depends on python + ] + } + + # HyperV needs restart, otherise, generalizing will fail + provisioner "windows-restart" { + restart_timeout = "30m" + pause_before = "2m" + } + + // this doesn't work yet + // provisioner "powershell" { + // elevated_user = build.User + // elevated_password = build.Password + // scripts = [for r in var.repos : "../../scripts/Clone-Repo.ps1 -Url '${r.url}' -Secret '${r.secret}'"] + // } + + provisioner "powershell" { + scripts = [ + "../../scripts/Disable-AutoLogon.ps1", + "../../scripts/Generalize-VM.ps1" + ] + } +} diff --git a/images/JavaEclipseBox/image.yml b/images/JavaEclipseBox/image.yml new file mode 100644 index 00000000..e1d822c7 --- /dev/null +++ b/images/JavaEclipseBox/image.yml @@ -0,0 +1,9 @@ +description: Windows 11 Enterprise + M365 Apps + Eclipse +publisher: Contoso +offer: DevBox +sku: win11-eclipse +version: 1.0.0 +os: Windows +replicaLocations: + - eastus + - westeurope diff --git a/images/JavaEclipseBox/variable.pkr.hcl b/images/JavaEclipseBox/variable.pkr.hcl new file mode 100644 index 00000000..428b01e5 --- /dev/null +++ b/images/JavaEclipseBox/variable.pkr.hcl @@ -0,0 +1,86 @@ +variable "branch" { + type = string + default = "" + description = "The branch to use for the build" +} + +variable "commit" { + type = string + default = "" + description = "The commit to use for the build" +} + +variable "galleryName" { + type = string + default = "" + description = "The name of the gallery to use for the build" +} + +variable "galleryResourceGroup" { + type = string + default = "" + description = "The resource group to use for the managed image" +} + +variable "name" { + type = string + default = "" + description = "The name of the image to use for the build" +} + +variable "replicaLocations" { + type = list(string) + default = [] + description = "The locations to replicate the image to" +} + +variable "resolvedResourceGroup" { + type = string + default = "" + description = "" +} + +variable "location" { + type = string + default = "" + description = "Azure datacenter in which your VM will build, if this is provided buildResourceGroup should be left blank" +} + +variable "tempResourceGroup" { + type = string + default = "" + description = "Name assigned to the temporary resource group created during the build. If this value is not set, a random value will be assigned. This resource group is deleted at the end of the build. If this is provided buildResourceGroup should be left blank" +} + +variable "buildResourceGroup" { + type = string + default = "" + description = "Specify an existing resource group to run the build in. If this is provided tempResourceGroup and location should not be provided" +} + +variable "subscription" { + type = string + default = "" + description = "The subscription to use for the build" +} + +variable "version" { + type = string + default = "" + description = "The version to use for the build" +} + +variable "identities" { + type = list(string) + default = [] + description = "One or more fully-qualified resource IDs of user assigned managed identities to be configured on the VM" +} + +variable "repos" { + type = list(object({ + url = string + secret = string + })) + default = [] + description = "The repositories to clone on the image" +} \ No newline at end of file diff --git a/scripts/Install-Eclipse.ps1 b/scripts/Install-Eclipse.ps1 new file mode 100644 index 00000000..b77c17a8 --- /dev/null +++ b/scripts/Install-Eclipse.ps1 @@ -0,0 +1,26 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +$ProgressPreference = 'SilentlyContinue' # hide any progress output + +$eclipseVersion = "2022-06" +$eclipseDownloadName = "eclipse-jee-${eclipseVersion}.exe" +$eclipseDownloadPath = Join-Path -Path $env:TEMP -ChildPath $eclipseDownloadName +$eclipseInstallPath = "C:\Program Files" + +Write-Host "[${env:username}] Downloading Eclipse ..." +$sourceUrl = "https://ftp.osuosl.org/pub/eclipse/technology/epp/downloads/release/${eclipseVersion}/R/eclipse-jee-${eclipseVersion}-R-win32-x86_64.zip" +#echo $sourceUrl +(new-object net.webclient).DownloadFile($sourceUrl, $eclipseDownloadPath) + +Write-Host "[${env:username}] Unzip Eclipse ..." + +Add-Type -AssemblyName System.IO.Compression.FileSystem +function Unzip +{ + param([string]$zipfile, [string]$outpath) + + [System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $outpath) +} + +Unzip $eclipseDownloadPath $eclipseInstallPath diff --git a/scripts/Install-GCloudCLI.ps1 b/scripts/Install-GCloudCLI.ps1 new file mode 100644 index 00000000..54eaebf1 --- /dev/null +++ b/scripts/Install-GCloudCLI.ps1 @@ -0,0 +1,20 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +# Reference: https://docs.microsoft.com/en-us/cli/azure/install-azure-cli-windows?tabs=azure-powershell + +$ProgressPreference = 'SilentlyContinue' # hide any progress output + +$installerName = 'GoogleCloudSDKInstaller..exe' +$installerPath = Join-Path -Path $env:TEMP -ChildPath $installerName + +Write-Host "[${env:username}] Downloading GCloud CLI ..." +(new-object net.webclient).DownloadFile('https://dl.google.com/dl/cloudsdk/channels/rapid/GoogleCloudSDKInstaller.exe', $installerPath) + +Write-Host "[${env:username}] Installing GCloud CLI ..." +$process = Start-Process -FilePath $installerPath -ArgumentList ` + "/S" ` + -NoNewWindow -Wait -PassThru + +exit $process.ExitCode + diff --git a/scripts/Install-Python3.8.ps1 b/scripts/Install-Python3.8.ps1 new file mode 100644 index 00000000..79d8d2d4 --- /dev/null +++ b/scripts/Install-Python3.8.ps1 @@ -0,0 +1,19 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +$ProgressPreference = 'SilentlyContinue' # hide any progress output + +$pythonVersion = "3.8.10" + +$installerName = "python-${pythonVersion}-amd64.exe" +$InstallerPath = Join-Path -Path $env:TEMP -ChildPath $installerName + +Write-Host "[${env:username}] Downloading Python ${pythonVersion} ..." +(new-object net.webclient).DownloadFile("https://www.python.org/ftp/python/${pythonVersion}/python-${pythonVersion}-amd64.exe", $InstallerPath) + +Write-Host "[${env:username}] Installing Python ${pythonVersion} ..." +$process = Start-Process -FilePath $installerPath -ArgumentList ` + "/quiet" ` + -NoNewWindow -Wait -PassThru + +exit $process.ExitCode \ No newline at end of file