diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index ce8b76d..ec3e945 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -85,7 +85,8 @@ RUN curl -L --fail -o /tmp/azcopy.tar.gz "https://aka.ms/downloadazcopy-v10-linu && chmod +x /usr/local/bin/azcopy ARG PAJV_VERSION="1.2.0" -RUN npm install -g pajv@${PAJV_VERSION} +RUN npm install -g pajv@${PAJV_VERSION} \ + && npm install -g yarn@1.22.22 # Install git - required for terraform's git modules RUN if [ "${INTERACTIVE}" = "false" ]; then \ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 971ed60..1635194 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -18,7 +18,7 @@ "DOCKER_GROUP_ID": "${localEnv:DOCKER_GROUP_ID}", "INTERACTIVE": "true", "UPSTREAM_REPO": "OxBRCInformatics/AzureTRE", - "UPSTREAM_REPO_VERSION": "tvstre-prod-main-v0.23.1", + "UPSTREAM_REPO_VERSION": "tvstre-prod-main-v0.25.12", "GITHUB_TOKEN": "" } }, diff --git a/.github/actions/devcontainer_run_command/action.yml b/.github/actions/devcontainer_run_command/action.yml index 8a474e4..ef17627 100644 --- a/.github/actions/devcontainer_run_command/action.yml +++ b/.github/actions/devcontainer_run_command/action.yml @@ -1,6 +1,7 @@ --- name: "run_command" description: "Run a command in a devcontainer" +# yamllint disable rule:line-length inputs: COMMAND: description: "The command you want to run in the Devcontainer." @@ -165,6 +166,14 @@ inputs: description: "When set to true removes the need for users to manually grant consent when creating new workspaces" required: false default: "false" + ENABLE_DNS_POLICY: + description: "Enable the DNS security policy" + required: false + default: "false" + ALLOWED_DNS: + description: "Add allowed domain name to the DNS security policy" + required: false + default: "[]" DOCKER_USERNAME: description: "Docker Hub username" required: true @@ -296,7 +305,7 @@ runs: -e UI_FOOTER_TEXT="${{ inputs.UI_FOOTER_TEXT }}" \ -e TF_VAR_resource_processor_number_processes_per_instance="${{ (inputs.RESOURCE_PROCESSOR_NUMBER_PROCESSES_PER_INSTANCE != '' && inputs.RESOURCE_PROCESSOR_NUMBER_PROCESSES_PER_INSTANCE) || 5 }}" \ - -e TF_VAR_firewall_sku=${{ inputs.FIREWALL_SKU }} \ + -e TF_VAR_firewall_sku=${{ inputs.FIREWALL_SKU != '' && inputs.FIREWALL_SKU || 'Standard' }} \ -e TF_VAR_app_gateway_sku=${{ inputs.APP_GATEWAY_SKU }} \ -e TF_VAR_enable_cmk_encryption="${{ (inputs.ENABLE_CMK_ENCRYPTION != '' && inputs.ENABLE_CMK_ENCRYPTION) || 'false' }}" \ @@ -307,6 +316,8 @@ runs: -e PRIVATE_AGENT_SUBNET_ID=${{ inputs.PRIVATE_AGENT_SUBNET_ID }} \ -e TF_VAR_private_agent_subnet_id=${{ inputs.PRIVATE_AGENT_SUBNET_ID }} \ -e TF_VAR_auto_grant_workspace_consent="${{ (inputs.AUTO_GRANT_WORKSPACE_CONSENT != '' && inputs.AUTO_GRANT_WORKSPACE_CONSENT) || 'false' }}" \ + -e TF_VAR_enable_dns_policy="${{ (inputs.ENABLE_DNS_POLICY != '' && inputs.ENABLE_DNS_POLICY) || 'false' }}" \ + -e TF_VAR_allowed_dns='${{ (toJson(inputs.ALLOWED_DNS) != '""' && inputs.ALLOWED_DNS) || '[]' }}' \ -e DOCKER_USERNAME="${{ inputs.DOCKER_USERNAME }}" \ -e DOCKER_TOKEN="${{ inputs.DOCKER_TOKEN }}" \ -e E2E_TESTS_NUMBER_PROCESSES="${{ inputs.E2E_TESTS_NUMBER_PROCESSES }}" \ diff --git a/.github/workflows/deploy_tre_reusable.yml b/.github/workflows/deploy_tre_reusable.yml index 2005075..2327479 100644 --- a/.github/workflows/deploy_tre_reusable.yml +++ b/.github/workflows/deploy_tre_reusable.yml @@ -1,5 +1,6 @@ --- name: Deploy Azure TRE Reusable +# yamllint disable rule:line-length rule:comments-indentation on: # yamllint disable-line rule:truthy workflow_call: @@ -281,7 +282,8 @@ jobs: az acr login --name "${{ secrets.CI_CACHE_ACR_NAME }}" && ec=0 && break || ec="$?" && sleep 10 done if [ "$ec" != 0 ]; then - echo "::error::Failed to login to ACR after 3 attempts. Make sure to run the pipleline on the main branch first." + echo "::error::Failed to login to ACR after 3 attempts. Make sure to run the" \ + "pipleline on the main branch first." fi # shellcheck disable=SC2242 (exit "$ec") @@ -406,6 +408,8 @@ jobs: USER_MANAGEMENT_ENABLED: ${{ vars.USER_MANAGEMENT_ENABLED }} PRIVATE_AGENT_SUBNET_ID: ${{ secrets.PRIVATE_AGENT_SUBNET_ID }} AUTO_GRANT_WORKSPACE_CONSENT: ${{ vars.AUTO_GRANT_WORKSPACE_CONSENT }} + ENABLE_DNS_POLICY: ${{ vars.ENABLE_DNS_POLICY }} + ALLOWED_DNS: ${{ vars.ALLOWED_DNS }} DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} @@ -781,7 +785,6 @@ jobs: TEST_ACCOUNT_CLIENT_SECRET: "${{ secrets.TEST_ACCOUNT_CLIENT_SECRET }}" TRE_ID: ${{ secrets.TRE_ID }} LOCATION: ${{ vars.LOCATION }} - FIREWALL_SKU: ${{ vars.FIREWALL_SKU}} DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} diff --git a/config.sample.yaml b/config.sample.yaml index ccac3d2..1d27f4a 100644 --- a/config.sample.yaml +++ b/config.sample.yaml @@ -68,6 +68,9 @@ tre: # Uncomment to deploy to a custom domain # custom_domain: __CHANGE_ME__ + + # Uncomment to enable vnet exception for the subnet to access private resources like TRE key vault and management storage account. + # private_agent_subnet_id: __CHANGE_ME__ authentication: aad_tenant_id: __CHANGE_ME__ # Setting AUTO_WORKSPACE_APP_REGISTRATION to false will: diff --git a/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/porter.yaml b/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/porter.yaml index dda3610..c70ddea 100644 --- a/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/porter.yaml +++ b/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/porter.yaml @@ -1,7 +1,7 @@ --- schemaVersion: 1.0.0 name: tre-service-guacamole-linuxvm-ouh2 -version: 1.1.8 +version: 1.2.0 description: "An Azure TRE User Resource Template for Guacamole (Linux)" dockerfile: Dockerfile.tmpl registry: azuretre @@ -44,20 +44,17 @@ custom: secure_boot_enabled: true vtpm_enabled: true "OUH Ubuntu 22.04 Data Science VM": - source_image_name: imgdef-linux-vm-custom + source_image_name: imgdef-linux-vm-custom-gen2 install_ui: true conda_config: true r_config: true docker_config: true - secure_boot_enabled: true - vtpm_enabled: true - # For information on using custom images, see README.me in the guacamole/user-resources folder - # "Custom Image From Gallery": - # source_image_name: your-image - # install_ui: true - # conda_config: true - # secure_boot_enabled: false - # vtpm_enabled: false + # secure_boot and vtpm are disabled because the image was captured from a Standard + # security type build VM. The image definition does not have SecurityType=TrustedLaunchSupported + # and enabling these flags would cause a validation error on deployment. + # Revisit when rebuilding from a Trusted Launch build VM. + secure_boot_enabled: false + vtpm_enabled: false credentials: @@ -102,7 +99,7 @@ parameters: default: "" type: string description: "Time of day to shutdown the VM (HHmm format)" - pattern: "^([01]?[0-9]|2[0-3])[0-5][0-9]$" + pattern: "^([01]?[0-9]|2[0-3])[0-5][0-9]?$" - name: shutdown_timezone type: string description: "Timezone for the shutdown schedule" @@ -183,9 +180,9 @@ outputs: mixins: - exec - terraform: - clientVersion: 1.9.8 + clientVersion: 1.14.3 - az: - clientVersion: 2.67.0 + clientVersion: 2.81.0 install: - terraform: @@ -329,7 +326,7 @@ start: - login flags: identity: - username: ${ bundle.credentials.azure_client_id } + client-id: ${ bundle.credentials.azure_client_id } - az: description: "Start the VM" arguments: @@ -365,7 +362,7 @@ stop: - login flags: identity: - username: ${ bundle.credentials.azure_client_id } + client-id: ${ bundle.credentials.azure_client_id } - az: description: "Stop the VM" arguments: @@ -404,7 +401,7 @@ reset_password: - login flags: identity: - username: ${ bundle.credentials.azure_client_id } + client-id: ${ bundle.credentials.azure_client_id } - exec: description: "Reset password and persist to keyvault" suppress-output: true diff --git a/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/template_schema.json b/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/template_schema.json index 0cad594..1ebd444 100644 --- a/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/template_schema.json +++ b/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/template_schema.json @@ -83,7 +83,14 @@ "title": "Enable Shutdown Schedule", "default": false, "description": "Enable automatic shutdown schedule for the VM" - } + }, + "assign_to_another_user": { + "type": "boolean", + "title": "Assign this VM to another user", + "description": "Check this box if you want to assign this VM to another user.", + "default": false, + "updateable": false + } }, "allOf": [ { @@ -112,31 +119,112 @@ "description": "Timezone for the shutdown schedule", "default": "UTC", "enum": [ - "UTC -12", - "UTC -11", - "UTC -10", - "UTC -9", - "UTC -8", - "UTC -7", - "UTC -6", - "UTC -5", - "UTC -4", - "UTC -3", - "UTC -2", - "UTC -1", + "Afghanistan Standard Time", + "Alaskan Standard Time", + "Arab Standard Time", + "Arabian Standard Time", + "Arabic Standard Time", + "Argentina Standard Time", + "Atlantic Standard Time", + "AUS Central Standard Time", + "AUS Eastern Standard Time", + "Azerbaijan Standard Time", + "Azores Standard Time", + "Bahia Standard Time", + "Bangladesh Standard Time", + "Belarus Standard Time", + "Canada Central Standard Time", + "Cape Verde Standard Time", + "Caucasus Standard Time", + "Cen. Australia Standard Time", + "Central America Standard Time", + "Central Asia Standard Time", + "Central Brazilian Standard Time", + "Central Europe Standard Time", + "Central European Standard Time", + "Central Pacific Standard Time", + "Central Standard Time (Mexico)", + "Central Standard Time", + "China Standard Time", + "Dateline Standard Time", + "E. Africa Standard Time", + "E. Australia Standard Time", + "E. Europe Standard Time", + "E. South America Standard Time", + "Eastern Standard Time (Mexico)", + "Eastern Standard Time", + "Egypt Standard Time", + "Ekaterinburg Standard Time", + "Fiji Standard Time", + "FLE Standard Time", + "Georgian Standard Time", + "GMT Standard Time", + "Greenland Standard Time", + "Greenwich Standard Time", + "GTB Standard Time", + "Hawaiian Standard Time", + "India Standard Time", + "Iran Standard Time", + "Israel Standard Time", + "Jordan Standard Time", + "Kaliningrad Standard Time", + "Korea Standard Time", + "Libya Standard Time", + "Line Islands Standard Time", + "Magadan Standard Time", + "Mauritius Standard Time", + "Middle East Standard Time", + "Montevideo Standard Time", + "Morocco Standard Time", + "Mountain Standard Time (Mexico)", + "Mountain Standard Time", + "Myanmar Standard Time", + "N. Central Asia Standard Time", + "Namibia Standard Time", + "Nepal Standard Time", + "New Zealand Standard Time", + "Newfoundland Standard Time", + "North Asia East Standard Time", + "North Asia Standard Time", + "Pacific SA Standard Time", + "Pacific Standard Time (Mexico)", + "Pacific Standard Time", + "Pakistan Standard Time", + "Paraguay Standard Time", + "Romance Standard Time", + "Russia Time Zone 10", + "Russia Time Zone 11", + "Russia Time Zone 3", + "Russian Standard Time", + "SA Eastern Standard Time", + "SA Pacific Standard Time", + "SA Western Standard Time", + "Samoa Standard Time", + "SE Asia Standard Time", + "Singapore Standard Time", + "South Africa Standard Time", + "Sri Lanka Standard Time", + "Syria Standard Time", + "Taipei Standard Time", + "Tasmania Standard Time", + "Tokyo Standard Time", + "Tonga Standard Time", + "Turkey Standard Time", + "Ulaanbaatar Standard Time", + "US Eastern Standard Time", + "US Mountain Standard Time", "UTC", - "UTC +1", - "UTC +2", - "UTC +3", - "UTC +4", - "UTC +5", - "UTC +6", - "UTC +7", - "UTC +8", - "UTC +9", - "UTC +10", - "UTC +11", - "UTC +12" + "UTC+12", + "UTC-02", + "UTC-11", + "Venezuela Standard Time", + "Vladivostok Standard Time", + "W. Australia Standard Time", + "W. Central Africa Standard Time", + "W. Europe Standard Time", + "West Asia Standard Time", + "West Pacific Standard Time", + "Yakutsk Standard Time" ] } }, @@ -145,6 +233,32 @@ "shutdown_timezone" ] } + }, + { + "if": { + "properties": { + "assign_to_another_user": { + "const": true + } + }, + "required": [ + "assign_to_another_user" + ] + }, + "then": { + "properties": { + "owner_id": { + "type": "string", + "title": "Owner ID", + "description": "Enter the Object ID of the user you want to assign this VM to.", + "default": false, + "updateable": false + } + }, + "required": [ + "owner_id" + ] + } } ], "uiSchema": { diff --git a/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/.terraform.lock.hcl b/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/.terraform.lock.hcl index 2724e7a..a4e9591 100644 --- a/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/.terraform.lock.hcl +++ b/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/.terraform.lock.hcl @@ -1,60 +1,62 @@ # This file is maintained automatically by "terraform init". # Manual edits may be lost in future updates. -provider "registry.terraform.io/hashicorp/azurerm" { - version = "3.117.0" - constraints = "3.117.0" +provider "registry.terraform.io/hashicorp/azuread" { + version = "3.7.0" + constraints = "3.7.0" hashes = [ - "h1:Ynfg+Iy7x6K8M6W1AhqXCe3wkoiqIQhROlca7C3KC3w=", - "zh:2e25f47492366821a786762369f0e0921cc9452d64bfd5075f6fdfcf1a9c6d70", - "zh:41eb34f2f7469bf3eb1019dfb0e7fc28256f809824016f4f8b9d691bf473b2ac", - "zh:48bb9c87b3d928da1abc1d3db75453c9725de4674c612daf3800160cc7145d30", - "zh:5d6b0de0bbd78943fcc65c53944ef4496329e247f434c6eab86ed051c5cea67b", - "zh:78c9f6fdb1206a89cf0e6706b4f46178169a93b6c964a4cad8a321058ccbd9b4", - "zh:793b702c352589d4360b580d4a1cf654a7439d2ad6bdb7bfea91de07bc4b0fac", - "zh:7ed687ff0a5509463a592f97431863574fe5cc80a34e395be06766215b8c6285", - "zh:955ba18789bd15592824eb426a8d0f38595bd09fffc6939c1c58933489c1a71e", - "zh:bf5949a55be0714cd9c8815d472eae4baa48ba06d0f6bf2b96775869acda8a54", - "zh:da5d31f635abd2c645ffc76d6176d73f646128e73720cc368247cc424975c127", - "zh:eed5a66d59883c9c56729b0a964a2b60d758ea7489ef3e920a6fbd48518ce5f5", - "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + "h1:xUoYzyYNcneRJDN8rlJ4tT+ZjebaDqvuNm6eMxKCeX4=", + "zh:01114beb07eb8415a563ca80a513e7fd11fd6f0670bdd960e9092e58464ab2b7", + "zh:1460c790ca28cc11c3efd4abec076fc5cdd4ae1cc654a45c69e10ca064e0a611", + "zh:161b6dd82cf279e7ac7a31c404f2ace491a5a9e7ec382ace91de2fd18f8c190b", + "zh:1c3e89cf19118fc07d7b04257251fc9897e722c16e0a0df7b07fcd261f8c12e7", + "zh:2eff0bea92c48be0221863ebf7752f491232088f4ec3cbe4da2ebf9f478fed1f", + "zh:69a689198fcac054da30fea6a8abce2f12b667f6f327c9bb388f2c307ee782fd", + "zh:6f5acf42d71a2f5157c54e67b2db9b080c4ebb27b812112c0c84241e66d94803", + "zh:8e0a58ff2c21aff372c66c679db48578be48e99f5cf6727f082cf4b629dbda6c", + "zh:9c88cc06ba7ee1f6e6b43af7e223c4d107fb77e5a984f1441fffb00c3693537f", + "zh:e8656e8143baac71029857c72899f8503cede41f2559c89ce29d21c0436c96fc", + "zh:e8e21913956a3f043c0e65cc95278bdc82f62ff33fd75a56b1919bd63fd14f8a", + "zh:f7f213dd61d1c75b8397d97660cf329870a241e88ec6e0dc07c60725d975252e", ] } -provider "registry.terraform.io/hashicorp/random" { - version = "3.4.3" - constraints = "3.4.3" +provider "registry.terraform.io/hashicorp/azurerm" { + version = "4.57.0" + constraints = "4.57.0" hashes = [ - "h1:xZGZf18JjMS06pFa4NErzANI98qi59SEcBsOcS2P2yQ=", - "zh:41c53ba47085d8261590990f8633c8906696fa0a3c4b384ff6a7ecbf84339752", - "zh:59d98081c4475f2ad77d881c4412c5129c56214892f490adf11c7e7a5a47de9b", - "zh:686ad1ee40b812b9e016317e7f34c0d63ef837e084dea4a1f578f64a6314ad53", + "h1:NhgHn/RyZRDXMa7pEQlGv/9B+wjk48E+lvgq4asFKHs=", + "zh:05e1cc7fee7829919b772ca6ce893d9c2abb3535ebff172df38f7358cdaf8f9e", + "zh:30122203abc381660582f989c9e53874bd9ff93e25476a5536ea0ae37dd51f4b", + "zh:4a90f008f7707d95f8f9aca90f140a9ca0e9506b0a6d436fe516de4026cacd86", + "zh:6d9e114b8aed06454b71fe91a0591cc6a16cd7acde3cb36a96e4aeaec06a315a", + "zh:7145c50facd9d40615fc63561ec21962feae3fa262239f9f1f1339581226104b", "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", - "zh:84103eae7251384c0d995f5a257c72b0096605048f757b749b7b62107a5dccb3", - "zh:8ee974b110adb78c7cd18aae82b2729e5124d8f115d484215fd5199451053de5", - "zh:9dd4561e3c847e45de603f17fa0c01ae14cae8c4b7b4e6423c9ef3904b308dda", - "zh:bb07bb3c2c0296beba0beec629ebc6474c70732387477a65966483b5efabdbc6", - "zh:e891339e96c9e5a888727b45b2e1bb3fcbdfe0fd7c5b4396e4695459b38c8cb1", - "zh:ea4739860c24dfeaac6c100b2a2e357106a89d18751f7693f3c31ecf6a996f8d", - "zh:f0c76ac303fd0ab59146c39bc121c5d7d86f878e9a69294e29444d4c653786f8", - "zh:f143a9a5af42b38fed328a161279906759ff39ac428ebcfe55606e05e1518b93", + "zh:95f60557f1bc4210ecc3c11e2f86fe983ed7e4af19036616b605887c1195f2ac", + "zh:9722b3ab879a3457588af5f0dcd65e997263affe4b829e60ecd59dbef6239e70", + "zh:b891f295b018d058e8c6f841d923d5b30ba13b8208f2c20aacc70ba48c5cc0da", + "zh:cb7ff113ca0bd91ab76f9f7a492d6ce9c911d6c4deb8c8e263e38322b5ff861e", + "zh:ec2950bf003d29bee3fa87ab073d57fe14a4da52e9fc646fec27798b700cc8af", + "zh:f69899d9e1d570a560cfa97aebec3edc2b106f2ebc15dfdee473294dd8756deb", ] } -provider "registry.terraform.io/hashicorp/template" { - version = "2.2.0" - constraints = "2.2.0" +provider "registry.terraform.io/hashicorp/cloudinit" { + version = "2.3.5" + constraints = "2.3.5" hashes = [ - "h1:94qn780bi1qjrbC3uQtjJh3Wkfwd5+tTtJHOb7KTg9w=", - "zh:01702196f0a0492ec07917db7aaa595843d8f171dc195f4c988d2ffca2a06386", - "zh:09aae3da826ba3d7df69efeb25d146a1de0d03e951d35019a0f80e4f58c89b53", - "zh:09ba83c0625b6fe0a954da6fbd0c355ac0b7f07f86c91a2a97849140fea49603", - "zh:0e3a6c8e16f17f19010accd0844187d524580d9fdb0731f675ffcf4afba03d16", - "zh:45f2c594b6f2f34ea663704cc72048b212fe7d16fb4cfd959365fa997228a776", - "zh:77ea3e5a0446784d77114b5e851c970a3dde1e08fa6de38210b8385d7605d451", - "zh:8a154388f3708e3df5a69122a23bdfaf760a523788a5081976b3d5616f7d30ae", - "zh:992843002f2db5a11e626b3fc23dc0c87ad3729b3b3cff08e32ffb3df97edbde", - "zh:ad906f4cebd3ec5e43d5cd6dc8f4c5c9cc3b33d2243c89c5fc18f97f7277b51d", - "zh:c979425ddb256511137ecd093e23283234da0154b7fa8b21c2687182d9aea8b2", + "h1:HCoabXm6NQwCivl1q24+l9VUufc2mFqNeulsQBA9iFg=", + "zh:17c20574de8eb925b0091c9b6a4d859e9d6e399cd890b44cfbc028f4f312ac7a", + "zh:348664d9a900f7baf7b091cf94d657e4c968b240d31d9e162086724e6afc19d5", + "zh:5a876a468ffabff0299f8348e719cb704daf81a4867f8c6892f3c3c4add2c755", + "zh:6ef97ee4c8c6a69a3d36746ba5c857cf4f4d78f32aa3d0e1ce68f2ece6a5dba5", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:8283e5a785e3c518a440f6ac6e7cc4fc07fe266bf34974246f4e2ef05762feda", + "zh:a44eb5077950168b571b7eb65491246c00f45409110f0f172cc3a7605f19dba9", + "zh:aa0806cbff72b49c1b389c0b8e6904586e5259c08dabb7cb5040418568146530", + "zh:bec4613c3beaad9a7be7ca99cdb2852073f782355b272892e6ee97a22856aec1", + "zh:d7fe368577b6c8d1ae44c751ed42246754c10305c7f001cc0109833e95aa107d", + "zh:df2409fc6a364b1f0a0f8a9cd8a86e61e80307996979ce3790243c4ce88f2915", + "zh:ed3c263396ff1f4d29639cc43339b655235acf4d06296a7c120a80e4e0fd6409", ] } diff --git a/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/data.tf b/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/data.tf index 07b1b70..cd3162a 100644 --- a/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/data.tf +++ b/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/data.tf @@ -44,28 +44,6 @@ data "azurerm_user_assigned_identity" "ws_encryption_identity" { resource_group_name = data.azurerm_resource_group.ws.name } -data "template_file" "get_apt_keys" { - template = file("${path.module}/get_apt_keys.sh") - vars = { - NEXUS_PROXY_URL = local.nexus_proxy_url - } -} - -data "template_file" "pypi_sources_config" { - template = file("${path.module}/pypi_sources_config.sh") - vars = { - nexus_proxy_url = local.nexus_proxy_url - } -} - -data "template_file" "apt_sources_config" { - template = file("${path.module}/apt_sources_config.yml") - vars = { - nexus_proxy_url = local.nexus_proxy_url - apt_sku = local.apt_sku - } -} - data "azurerm_storage_account" "stg" { name = local.storage_name resource_group_name = data.azurerm_resource_group.ws.name @@ -74,4 +52,4 @@ data "azurerm_storage_account" "stg" { data "azuread_user" "user" { object_id = var.owner_id count = var.admin_username == "" ? 1 : 0 -} \ No newline at end of file +} diff --git a/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/linuxvm.tf b/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/linuxvm.tf index b876291..2606c6e 100644 --- a/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/linuxvm.tf +++ b/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/linuxvm.tf @@ -37,7 +37,7 @@ resource "azurerm_linux_virtual_machine" "linuxvm" { secure_boot_enabled = local.secure_boot_enabled vtpm_enabled = local.vtpm_enabled - custom_data = data.template_cloudinit_config.config.rendered + custom_data = data.cloudinit_config.config.rendered # set source_image_id/reference depending on the config for the selected image source_image_id = local.selected_image_source_id @@ -91,46 +91,28 @@ resource "azurerm_disk_encryption_set" "linuxvm_disk_encryption" { } } -data "template_cloudinit_config" "config" { +data "cloudinit_config" "config" { gzip = true base64_encode = true part { content_type = "text/x-shellscript" - content = data.template_file.get_apt_keys.rendered + content = local.get_apt_keys_content } part { content_type = "text/cloud-config" - content = data.template_file.apt_sources_config.rendered + content = local.apt_sources_config_content } part { content_type = "text/x-shellscript" - content = data.template_file.pypi_sources_config.rendered + content = local.pypi_sources_config_content } part { content_type = "text/x-shellscript" - content = data.template_file.vm_config.rendered - } -} - -data "template_file" "vm_config" { - template = file("${path.module}/vm_config.sh") - vars = { - INSTALL_UI = local.selected_image.install_ui ? 1 : 0 - SHARED_STORAGE_ACCESS = tobool(var.shared_storage_access) ? 1 : 0 - STORAGE_ACCOUNT_NAME = data.azurerm_storage_account.stg.name - STORAGE_ACCOUNT_KEY = data.azurerm_storage_account.stg.primary_access_key - HTTP_ENDPOINT = data.azurerm_storage_account.stg.primary_file_endpoint - FILESHARE_NAME = var.shared_storage_access ? var.shared_storage_name : "" - NEXUS_PROXY_URL = local.nexus_proxy_url - CONDA_CONFIG = local.selected_image.conda_config ? 1 : 0 - R_CONFIG = local.selected_image.r_config ? 1 : 0 - DOCKER_CONFIG = local.selected_image.docker_config ? 1 : 0 - VM_USER = local.admin_username - APT_SKU = replace(local.apt_sku, ".", "") + content = local.vm_config_content } } diff --git a/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/locals.tf b/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/locals.tf index 6e41e82..11c702e 100644 --- a/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/locals.tf +++ b/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/locals.tf @@ -40,4 +40,31 @@ locals { apt_sku = length(local.selected_image_source_refs) > 0 ? local.selected_image_source_refs[0]["apt_sku"] : "22.04" cmk_name = "tre-encryption-${local.workspace_resource_name_suffix}" encryption_identity_name = "id-encryption-${var.tre_id}-${local.short_workspace_id}" + + get_apt_keys_content = templatefile("${path.module}/get_apt_keys.sh", { + NEXUS_PROXY_URL = local.nexus_proxy_url + }) + + pypi_sources_config_content = templatefile("${path.module}/pypi_sources_config.sh", { + nexus_proxy_url = local.nexus_proxy_url + }) + + apt_sources_config_content = templatefile("${path.module}/apt_sources_config.yml", { + nexus_proxy_url = local.nexus_proxy_url + apt_sku = local.apt_sku + }) + + vm_config_content = templatefile("${path.module}/vm_config.sh", { + INSTALL_UI = local.selected_image.install_ui ? 1 : 0 + SHARED_STORAGE_ACCESS = tobool(var.shared_storage_access) ? 1 : 0 + STORAGE_ACCOUNT_NAME = data.azurerm_storage_account.stg.name + STORAGE_ACCOUNT_KEY = data.azurerm_storage_account.stg.primary_access_key + HTTP_ENDPOINT = data.azurerm_storage_account.stg.primary_file_endpoint + FILESHARE_NAME = var.shared_storage_access ? var.shared_storage_name : "" + NEXUS_PROXY_URL = local.nexus_proxy_url + CONDA_CONFIG = local.selected_image.conda_config ? 1 : 0 + VM_USER = local.admin_username + APT_SKU = replace(local.apt_sku, ".", "") + }) + } diff --git a/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/main.tf b/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/main.tf index daf9bb6..0c697e6 100644 --- a/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/main.tf +++ b/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/main.tf @@ -3,19 +3,19 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "=3.117.0" + version = "= 4.57.0" } - template = { - source = "hashicorp/template" - version = "=2.2.0" + cloudinit = { + source = "hashicorp/cloudinit" + version = "= 2.3.5" } random = { source = "hashicorp/random" - version = "=3.4.3" + version = "= 3.7.2" } azuread = { source = "hashicorp/azuread" - version = "3.1.0" + version = "= 3.7.0" } } backend "azurerm" { @@ -49,4 +49,4 @@ provider "azuread" { client_id = var.auth_client_id client_secret = var.auth_client_secret tenant_id = var.auth_tenant_id -} \ No newline at end of file +} diff --git a/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/vm_config.sh b/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/vm_config.sh index 280622c..ff36c16 100644 --- a/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/vm_config.sh +++ b/templates/workspace_services/guacamole/user_resources/guacamole-azure-linuxvm-ouh2/terraform/vm_config.sh @@ -1,161 +1,56 @@ #!/bin/bash - set -o errexit set -o pipefail set -o nounset # Uncomment this line to see each command for debugging (careful: this will show secrets!) # set -o xtrace - -# Remove apt sources not included in sources.list file -sudo rm -f /etc/apt/sources.list.d/* - -# # Fix sources.list file to all be [trusted=yes] -# sudo sed -i 's|\[signed-by=[^]]*\]|[trusted=yes]|g' /etc/apt/sources.list - -# Update apt packages from configured Nexus sources -echo "init_vm.sh: START" -sudo apt update || true -sudo apt upgrade -y -sudo apt install -y gnupg software-properties-common apt-transport-https wget dirmngr gdebi-core debconf-utils -sudo apt-get update || true - -## Desktop -echo "init_vm.sh: Desktop" -sudo systemctl start gdm3 || true -DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true dpkg-reconfigure gdm3 || true -sudo apt install -y xfce4 xfce4-goodies xorg dbus-x11 x11-xserver-utils -echo /usr/sbin/gdm3 > /etc/X11/default-display-manager - -## Python 3.8 and Jupyter -sudo apt install -y jupyter-notebook microsoft-edge-dev - -# Azure Storage Explorer -sudo apt-get remove -y dotnet-host-7.0 -sudo apt-get remove -y dotnet-sdk-7.0 -sudo apt-get install -y dotnet-sdk-8.0 -sudo apt install gnome-keyring -y - -sudo chmod 666 /etc/profile - -echo "export DOTNET_ROOT=/usr/share/dotnet -export PATH=$PATH:/usr/share/dotnet -" | sudo tee -a /etc/profile - -sudo chmod 644 /etc/profile - -wget -q "${NEXUS_PROXY_URL}"/repository/microsoft-download/A/E/3/AE32C485-B62B-4437-92F7-8B6B2C48CB40/StorageExplorer-linux-x64.tar.gz -P /tmp -sudo mkdir /opt/storage-explorer -sudo tar xvf /tmp/StorageExplorer-linux-x64.tar.gz -C /opt/storage-explorer -sudo chmod +x /opt/storage-explorer/* - -sudo tee /usr/share/applications/storage-explorer.desktop << END -[Desktop Entry] -Name=Storage Explorer -Comment=Azure Storage Explorer -Exec=/opt/storage-explorer/StorageExplorer -Icon=/opt/storage-explorer/resources/app/out/app/icon.png -Terminal=false -Type=Application -StartupNotify=false -StartupWMClass=Code -Categories=Development; -END - - -# Make sure xrdp service starts up with the system -# sudo systemctl enable xrdp - +# --------------------------------------------------------------------------- +# NOTE: The following are pre-configured in the VM image and are NOT repeated here: +# - apt sources (Nexus proxied repositories only — no direct internet sources) +# - Base package installs (gnupg, wget, gdebi-core, autofs, cifs-utils, etc.) +# - Desktop environment (XFCE, xorg, dbus-x11) +# - Azure Storage Explorer (installed at /opt/storage-explorer with desktop entry) +# - pip (/etc/pip.conf) → Nexus PyPI proxy +# - conda channels → Nexus conda proxy +# - R repo (/etc/R/Rprofile.site) → Nexus R proxy +# - Docker daemon (/etc/docker/daemon.json) → Nexus registry mirror +# - Chrome (replaces Edge) +# - Jupyter (available via VS Code Jupyter extension) +# - xrdp session defaults (/etc/skel/.xsession) +# - xfce4-screensaver lock screen disabled (/etc/skel/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-screensaver.xml) +# --------------------------------------------------------------------------- +# --------------------------------------------------------------------------- +# Shared storage mount (per-workspace credentials, autofs/CIFS) +# --------------------------------------------------------------------------- if [ "${SHARED_STORAGE_ACCESS}" -eq 1 ]; then - # Install required packages - sudo apt-get install -y autofs cifs-utils - - # Pass in required variables storageAccountName="${STORAGE_ACCOUNT_NAME}" storageAccountKey="${STORAGE_ACCOUNT_KEY}" httpEndpoint="${HTTP_ENDPOINT}" fileShareName="${FILESHARE_NAME}" mntRoot="/fileshares" credentialRoot="/etc/smbcredentials" - mntPath="$mntRoot/$fileShareName" # shellcheck disable=SC2308 smbPath=$(echo "$httpEndpoint" | cut -c7-"$(expr length "$httpEndpoint")")$fileShareName smbCredentialFile="$credentialRoot/$storageAccountName.cred" - - # Create required file paths sudo mkdir -p "$mntPath" sudo mkdir -p "/etc/smbcredentials" sudo mkdir -p $mntRoot - - ### Auto FS to persist storage - # Create credential file if [ ! -f "$smbCredentialFile" ]; then echo "username=$storageAccountName" | sudo tee "$smbCredentialFile" > /dev/null echo "password=$storageAccountKey" | sudo tee -a "$smbCredentialFile" > /dev/null else echo "The credential file $smbCredentialFile already exists, and was not modified." fi - - # Change permissions on the credential file so only root can read or modify the password file. sudo chmod 600 "$smbCredentialFile" - - # Configure autofs echo "$fileShareName -fstype=cifs,rw,file_mode=0777,dir_mode=0777,credentials=$smbCredentialFile :$smbPath" | sudo tee /etc/auto.fileshares > /dev/null echo "$mntRoot /etc/auto.fileshares --timeout=60" | sudo tee /etc/auto.master > /dev/null - - # Restart service to register changes sudo systemctl restart autofs - - # Autofs mounts when accessed for 60 seconds. Folder created for constant visible mount + # Symlink for constant visible mount point sudo ln -s "$mntPath" "/$fileShareName" fi - -# R config -if [ "${R_CONFIG}" == "true" ]; then - sudo echo -e "local({\n r <- getOption(\"repos\")\n r[\"Nexus\"] <- \"""${NEXUS_PROXY_URL}/repository/r-proxy/\"\n options(repos = r)\n})" | sudo tee /etc/R/Rprofile.site -fi - -### Anaconda Config -if [ "${CONDA_CONFIG}" == "true" ]; then - export PATH="/opt/anaconda/condabin":$PATH - export PATH="/opt/anaconda/bin":$PATH - export PATH="/opt/anaconda/envs/py38_default/bin":$PATH - conda config - conda config --add channels "${NEXUS_PROXY_URL}"/repository/conda-mirror/main/ --system - conda config --add channels "${NEXUS_PROXY_URL}"/repository/conda-repo/main/ --system - conda config --remove channels defaults --system - conda config --set channel_alias "${NEXUS_PROXY_URL}"/repository/conda-mirror/ --system -fi - -# Docker config -if [ "${DOCKER_CONFIG}" == "true" ]; then - # Create Docker config directory if it doesn't exist - sudo mkdir -p /etc/docker/ - # Configure Docker registry mirrors - jq -n --arg proxy "${NEXUS_PROXY_URL}:8083" '{"registry-mirrors": [$proxy]}' | sudo tee /etc/docker/daemon.json > /dev/null - # Restart Docker service to apply configuration - sudo systemctl restart docker -fi - -## Prevent screen timeout and lock screen -echo "init_vm.sh: Disabling lock screen" -# Remove xfce4-screensaver (to disable screen saver) -sudo apt-get remove xfce4-screensaver -y - -## Install xrdp so Guacamole can connect via RDP -echo "init_vm.sh: xrdp" -sudo apt install -y xrdp xorgxrdp xfce4-session -sudo adduser xrdp ssl-cert -sudo -u "${VM_USER}" -i bash -c 'echo xfce4-session > ~/.xsession' -sudo -u "${VM_USER}" -i bash -c 'echo xset s off >> ~/.xsession' -sudo -u "${VM_USER}" -i bash -c 'echo xset -dpms >> ~/.xsession' - -# Fix for blank screen on DSVM (/sh -> /bash due to conflict with profile.d scripts) -sudo sed -i 's|!/bin/sh|!/bin/bash|g' /etc/xrdp/startwm.sh - -# Make sure xrdp service starts up with the system -sudo systemctl enable xrdp -sudo service xrdp restart - -## Cleanup -echo "init_vm.sh: Cleanup" +# --------------------------------------------------------------------------- +# Docker — restart to ensure service is running +# daemon.json (Nexus registry mirror) is already written in the image +# --------------------------------------------------------------------------- +sudo systemctl restart docker \ No newline at end of file diff --git a/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/porter.yaml b/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/porter.yaml index 558a0d8..b038f30 100644 --- a/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/porter.yaml +++ b/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/porter.yaml @@ -1,8 +1,8 @@ --- schemaVersion: 1.0.0 name: tre-service-guacamole-windowsvm-ouh2 -version: 1.1.5 -description: "An Azure TRE User Resource Template for Guacamole (Windows 10)" +version: 1.1.8 +description: "An Azure TRE User Resource Template for Guacamole (Windows)" dockerfile: Dockerfile.tmpl registry: azuretre @@ -43,21 +43,20 @@ custom: source_image_reference: publisher: microsoftwindowsdesktop offer: windows-11 - sku: win11-24h2-pro + sku: win11-25h2-pro version: latest conda_config: false secure_boot_enabled: true vtpm_enabled: true "OUH Server 2019 Data Science VM": - source_image_name: OUHWindowsImage - install_ui: true + source_image_name: imgdef-windows-vm-custom-gen2 conda_config: true - secure_boot_enabled: false # dsvm-win-2019 is not a gen2 image + # secure_boot and vtpm are disabled because the image was captured from a Standard + # security type build VM. The image definition does not have SecurityType=TrustedLaunchSupported + # and enabling these flags would cause a validation error on deployment. + # Revisit when rebuilding from a Trusted Launch build VM. + secure_boot_enabled: false vtpm_enabled: false - # For information on using custom images, see README.me in the guacamole/user-resources folder - # "Custom Image From Gallery": - # source_image_name: your-image - # conda_config: true credentials: - name: azure_tenant_id @@ -114,7 +113,7 @@ parameters: default: false - name: os_image type: string - default: "Windows 10" + default: "OUH Server 2019 Data Science VM" - name: admin_username type: string default: "" @@ -137,6 +136,19 @@ parameters: - name: key_store_id type: string default: "" + - name: enable_shutdown_schedule + type: boolean + default: false + description: "Enable automatic shutdown schedule for the VM" + - name: shutdown_time + default: "" + type: string + description: "Time of day to shutdown the VM (HHmm format)" + pattern: "^([01]?[0-9]|2[0-3])[0-5][0-9]?$" + - name: shutdown_timezone + type: string + description: "Timezone for the shutdown schedule" + default: "UTC" outputs: - name: ip @@ -165,9 +177,9 @@ outputs: mixins: - exec - terraform: - clientVersion: 1.9.8 + clientVersion: 1.14.3 - az: - clientVersion: 2.67.0 + clientVersion: 2.81.0 install: - terraform: @@ -183,6 +195,9 @@ install: shared_storage_access: ${ bundle.parameters.shared_storage_access } shared_storage_name: ${ bundle.parameters.shared_storage_name } image_gallery_id: ${ bundle.parameters.image_gallery_id } + enable_shutdown_schedule: ${ bundle.parameters.enable_shutdown_schedule } + shutdown_time: ${ bundle.parameters.shutdown_time } + shutdown_timezone: ${ bundle.parameters.shutdown_timezone } owner_id: ${ bundle.parameters.owner_id } enable_cmk_encryption: ${ bundle.parameters.enable_cmk_encryption } key_store_id: ${ bundle.parameters.key_store_id } @@ -216,6 +231,9 @@ upgrade: shared_storage_access: ${ bundle.parameters.shared_storage_access } shared_storage_name: ${ bundle.parameters.shared_storage_name } image_gallery_id: ${ bundle.parameters.image_gallery_id } + enable_shutdown_schedule: ${ bundle.parameters.enable_shutdown_schedule } + shutdown_time: ${ bundle.parameters.shutdown_time } + shutdown_timezone: ${ bundle.parameters.shutdown_timezone } owner_id: ${ bundle.parameters.owner_id } enable_cmk_encryption: ${ bundle.parameters.enable_cmk_encryption } key_store_id: ${ bundle.parameters.key_store_id } @@ -261,6 +279,9 @@ uninstall: shared_storage_access: ${ bundle.parameters.shared_storage_access } shared_storage_name: ${ bundle.parameters.shared_storage_name } image_gallery_id: ${ bundle.parameters.image_gallery_id } + enable_shutdown_schedule: ${ bundle.parameters.enable_shutdown_schedule } + shutdown_time: ${ bundle.parameters.shutdown_time } + shutdown_timezone: ${ bundle.parameters.shutdown_timezone } owner_id: ${ bundle.parameters.owner_id } enable_cmk_encryption: ${ bundle.parameters.enable_cmk_encryption } key_store_id: ${ bundle.parameters.key_store_id } @@ -302,7 +323,7 @@ start: - login flags: identity: - username: ${ bundle.credentials.azure_client_id } + client-id: ${ bundle.credentials.azure_client_id } - az: description: "Start the VM" arguments: @@ -338,7 +359,7 @@ stop: - login flags: identity: - username: ${ bundle.credentials.azure_client_id } + client-id: ${ bundle.credentials.azure_client_id } - az: description: "Stop the VM" arguments: @@ -377,7 +398,7 @@ reset_password: - login flags: identity: - username: ${ bundle.credentials.azure_client_id } + client-id: ${ bundle.credentials.azure_client_id } - exec: description: "Reset password and persist to keyvault" suppress-output: true diff --git a/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/template_schema.json b/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/template_schema.json index 4816993..c75cbc4 100644 --- a/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/template_schema.json +++ b/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/template_schema.json @@ -7,7 +7,8 @@ "required": [ ], "authorizedRoles": [ - "WorkspaceOwner", "WorkspaceResearcher" + "WorkspaceOwner", + "WorkspaceResearcher" ], "properties": { "display_name": { @@ -70,16 +71,199 @@ "updateable": true }, "shared_storage_access": { - "$id": "#/properties/shared_storage_access", - "type": "boolean", - "title": "Shared storage", - "default": true, - "description": "Enable access to shared storage" + "$id": "#/properties/shared_storage_access", + "type": "boolean", + "title": "Shared storage", + "default": true, + "description": "Enable access to shared storage" + }, + "enable_shutdown_schedule": { + "$id": "#/properties/enable_shutdown_schedule", + "type": "boolean", + "title": "Enable Shutdown Schedule", + "default": false, + "description": "Enable automatic shutdown schedule for the VM" + }, + "assign_to_another_user": { + "type": "boolean", + "title": "Assign this VM to another user", + "description": "Check this box if you want to assign this VM to another user.", + "default": false, + "updateable": false + } + }, + "allOf": [ + { + "if": { + "properties": { + "enable_shutdown_schedule": { + "const": true + } + }, + "required": [ + "enable_shutdown_schedule" + ] + }, + "then": { + "properties": { + "shutdown_time": { + "type": "string", + "title": "Shutdown Time", + "description": "Time of day to shutdown the VM (HHmm format), e.g. 1800", + "pattern": "^([01]?[0-9]|2[0-3])[0-5][0-9]$", + "default": "1800" + }, + "shutdown_timezone": { + "type": "string", + "title": "Shutdown Timezone", + "description": "Timezone for the shutdown schedule", + "default": "UTC", + "enum": [ + "Afghanistan Standard Time", + "Alaskan Standard Time", + "Arab Standard Time", + "Arabian Standard Time", + "Arabic Standard Time", + "Argentina Standard Time", + "Atlantic Standard Time", + "AUS Central Standard Time", + "AUS Eastern Standard Time", + "Azerbaijan Standard Time", + "Azores Standard Time", + "Bahia Standard Time", + "Bangladesh Standard Time", + "Belarus Standard Time", + "Canada Central Standard Time", + "Cape Verde Standard Time", + "Caucasus Standard Time", + "Cen. Australia Standard Time", + "Central America Standard Time", + "Central Asia Standard Time", + "Central Brazilian Standard Time", + "Central Europe Standard Time", + "Central European Standard Time", + "Central Pacific Standard Time", + "Central Standard Time (Mexico)", + "Central Standard Time", + "China Standard Time", + "Dateline Standard Time", + "E. Africa Standard Time", + "E. Australia Standard Time", + "E. Europe Standard Time", + "E. South America Standard Time", + "Eastern Standard Time (Mexico)", + "Eastern Standard Time", + "Egypt Standard Time", + "Ekaterinburg Standard Time", + "Fiji Standard Time", + "FLE Standard Time", + "Georgian Standard Time", + "GMT Standard Time", + "Greenland Standard Time", + "Greenwich Standard Time", + "GTB Standard Time", + "Hawaiian Standard Time", + "India Standard Time", + "Iran Standard Time", + "Israel Standard Time", + "Jordan Standard Time", + "Kaliningrad Standard Time", + "Korea Standard Time", + "Libya Standard Time", + "Line Islands Standard Time", + "Magadan Standard Time", + "Mauritius Standard Time", + "Middle East Standard Time", + "Montevideo Standard Time", + "Morocco Standard Time", + "Mountain Standard Time (Mexico)", + "Mountain Standard Time", + "Myanmar Standard Time", + "N. Central Asia Standard Time", + "Namibia Standard Time", + "Nepal Standard Time", + "New Zealand Standard Time", + "Newfoundland Standard Time", + "North Asia East Standard Time", + "North Asia Standard Time", + "Pacific SA Standard Time", + "Pacific Standard Time (Mexico)", + "Pacific Standard Time", + "Pakistan Standard Time", + "Paraguay Standard Time", + "Romance Standard Time", + "Russia Time Zone 10", + "Russia Time Zone 11", + "Russia Time Zone 3", + "Russian Standard Time", + "SA Eastern Standard Time", + "SA Pacific Standard Time", + "SA Western Standard Time", + "Samoa Standard Time", + "SE Asia Standard Time", + "Singapore Standard Time", + "South Africa Standard Time", + "Sri Lanka Standard Time", + "Syria Standard Time", + "Taipei Standard Time", + "Tasmania Standard Time", + "Tokyo Standard Time", + "Tonga Standard Time", + "Turkey Standard Time", + "Ulaanbaatar Standard Time", + "US Eastern Standard Time", + "US Mountain Standard Time", + "UTC", + "UTC+12", + "UTC-02", + "UTC-11", + "Venezuela Standard Time", + "Vladivostok Standard Time", + "W. Australia Standard Time", + "W. Central Africa Standard Time", + "W. Europe Standard Time", + "West Asia Standard Time", + "West Pacific Standard Time", + "Yakutsk Standard Time" + ] + } + }, + "required": [ + "shutdown_time", + "shutdown_timezone" + ] } }, - "uiSchema": { + { + "if": { + "properties": { + "assign_to_another_user": { + "const": true + } + }, + "required": [ + "assign_to_another_user" + ] + }, + "then": { + "properties": { + "owner_id": { + "type": "string", + "title": "Owner ID", + "description": "Enter the Object ID of the user you want to assign this VM to.", + "default": false, + "updateable": false + } + }, + "required": [ + "owner_id" + ] + } + } + ], + "uiSchema": { "admin_username": { "classNames": "tre-hidden" } - } + } } diff --git a/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/terraform/.terraform.lock.hcl b/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/terraform/.terraform.lock.hcl index 37741e4..d4a09cc 100644 --- a/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/terraform/.terraform.lock.hcl +++ b/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/terraform/.terraform.lock.hcl @@ -1,42 +1,62 @@ # This file is maintained automatically by "terraform init". # Manual edits may be lost in future updates. +provider "registry.terraform.io/hashicorp/azuread" { + version = "3.7.0" + constraints = "3.7.0" + hashes = [ + "h1:xUoYzyYNcneRJDN8rlJ4tT+ZjebaDqvuNm6eMxKCeX4=", + "zh:01114beb07eb8415a563ca80a513e7fd11fd6f0670bdd960e9092e58464ab2b7", + "zh:1460c790ca28cc11c3efd4abec076fc5cdd4ae1cc654a45c69e10ca064e0a611", + "zh:161b6dd82cf279e7ac7a31c404f2ace491a5a9e7ec382ace91de2fd18f8c190b", + "zh:1c3e89cf19118fc07d7b04257251fc9897e722c16e0a0df7b07fcd261f8c12e7", + "zh:2eff0bea92c48be0221863ebf7752f491232088f4ec3cbe4da2ebf9f478fed1f", + "zh:69a689198fcac054da30fea6a8abce2f12b667f6f327c9bb388f2c307ee782fd", + "zh:6f5acf42d71a2f5157c54e67b2db9b080c4ebb27b812112c0c84241e66d94803", + "zh:8e0a58ff2c21aff372c66c679db48578be48e99f5cf6727f082cf4b629dbda6c", + "zh:9c88cc06ba7ee1f6e6b43af7e223c4d107fb77e5a984f1441fffb00c3693537f", + "zh:e8656e8143baac71029857c72899f8503cede41f2559c89ce29d21c0436c96fc", + "zh:e8e21913956a3f043c0e65cc95278bdc82f62ff33fd75a56b1919bd63fd14f8a", + "zh:f7f213dd61d1c75b8397d97660cf329870a241e88ec6e0dc07c60725d975252e", + ] +} + provider "registry.terraform.io/hashicorp/azurerm" { - version = "3.117.0" - constraints = "3.117.0" + version = "4.57.0" + constraints = "4.57.0" hashes = [ - "h1:Ynfg+Iy7x6K8M6W1AhqXCe3wkoiqIQhROlca7C3KC3w=", - "zh:2e25f47492366821a786762369f0e0921cc9452d64bfd5075f6fdfcf1a9c6d70", - "zh:41eb34f2f7469bf3eb1019dfb0e7fc28256f809824016f4f8b9d691bf473b2ac", - "zh:48bb9c87b3d928da1abc1d3db75453c9725de4674c612daf3800160cc7145d30", - "zh:5d6b0de0bbd78943fcc65c53944ef4496329e247f434c6eab86ed051c5cea67b", - "zh:78c9f6fdb1206a89cf0e6706b4f46178169a93b6c964a4cad8a321058ccbd9b4", - "zh:793b702c352589d4360b580d4a1cf654a7439d2ad6bdb7bfea91de07bc4b0fac", - "zh:7ed687ff0a5509463a592f97431863574fe5cc80a34e395be06766215b8c6285", - "zh:955ba18789bd15592824eb426a8d0f38595bd09fffc6939c1c58933489c1a71e", - "zh:bf5949a55be0714cd9c8815d472eae4baa48ba06d0f6bf2b96775869acda8a54", - "zh:da5d31f635abd2c645ffc76d6176d73f646128e73720cc368247cc424975c127", - "zh:eed5a66d59883c9c56729b0a964a2b60d758ea7489ef3e920a6fbd48518ce5f5", - "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + "h1:NhgHn/RyZRDXMa7pEQlGv/9B+wjk48E+lvgq4asFKHs=", + "zh:05e1cc7fee7829919b772ca6ce893d9c2abb3535ebff172df38f7358cdaf8f9e", + "zh:30122203abc381660582f989c9e53874bd9ff93e25476a5536ea0ae37dd51f4b", + "zh:4a90f008f7707d95f8f9aca90f140a9ca0e9506b0a6d436fe516de4026cacd86", + "zh:6d9e114b8aed06454b71fe91a0591cc6a16cd7acde3cb36a96e4aeaec06a315a", + "zh:7145c50facd9d40615fc63561ec21962feae3fa262239f9f1f1339581226104b", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:95f60557f1bc4210ecc3c11e2f86fe983ed7e4af19036616b605887c1195f2ac", + "zh:9722b3ab879a3457588af5f0dcd65e997263affe4b829e60ecd59dbef6239e70", + "zh:b891f295b018d058e8c6f841d923d5b30ba13b8208f2c20aacc70ba48c5cc0da", + "zh:cb7ff113ca0bd91ab76f9f7a492d6ce9c911d6c4deb8c8e263e38322b5ff861e", + "zh:ec2950bf003d29bee3fa87ab073d57fe14a4da52e9fc646fec27798b700cc8af", + "zh:f69899d9e1d570a560cfa97aebec3edc2b106f2ebc15dfdee473294dd8756deb", ] } provider "registry.terraform.io/hashicorp/random" { - version = "3.5.1" - constraints = "3.5.1" + version = "3.7.2" + constraints = "3.7.2" hashes = [ - "h1:VSnd9ZIPyfKHOObuQCaKfnjIHRtR7qTw19Rz8tJxm+k=", - "zh:04e3fbd610cb52c1017d282531364b9c53ef72b6bc533acb2a90671957324a64", - "zh:119197103301ebaf7efb91df8f0b6e0dd31e6ff943d231af35ee1831c599188d", - "zh:4d2b219d09abf3b1bb4df93d399ed156cadd61f44ad3baf5cf2954df2fba0831", - "zh:6130bdde527587bbe2dcaa7150363e96dbc5250ea20154176d82bc69df5d4ce3", - "zh:6cc326cd4000f724d3086ee05587e7710f032f94fc9af35e96a386a1c6f2214f", + "h1:356j/3XnXEKr9nyicLUufzoF4Yr6hRy481KIxRVpK0c=", + "zh:14829603a32e4bc4d05062f059e545a91e27ff033756b48afbae6b3c835f508f", + "zh:1527fb07d9fea400d70e9e6eb4a2b918d5060d604749b6f1c361518e7da546dc", + "zh:1e86bcd7ebec85ba336b423ba1db046aeaa3c0e5f921039b3f1a6fc2f978feab", + "zh:24536dec8bde66753f4b4030b8f3ef43c196d69cccbea1c382d01b222478c7a3", + "zh:29f1786486759fad9b0ce4fdfbbfece9343ad47cd50119045075e05afe49d212", + "zh:4d701e978c2dd8604ba1ce962b047607701e65c078cb22e97171513e9e57491f", "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", - "zh:b6d88e1d28cf2dfa24e9fdcc3efc77adcdc1c3c3b5c7ce503a423efbdd6de57b", - "zh:ba74c592622ecbcef9dc2a4d81ed321c4e44cddf7da799faa324da9bf52a22b2", - "zh:c7c5cde98fe4ef1143bd1b3ec5dc04baf0d4cc3ca2c5c7d40d17c0e9b2076865", - "zh:dac4bad52c940cd0dfc27893507c1e92393846b024c5a9db159a93c534a3da03", - "zh:de8febe2a2acd9ac454b844a4106ed295ae9520ef54dc8ed2faf29f12716b602", - "zh:eab0d0495e7e711cca367f7d4df6e322e6c562fc52151ec931176115b83ed014", + "zh:7b8434212eef0f8c83f5a90c6d76feaf850f6502b61b53c329e85b3b281cba34", + "zh:ac8a23c212258b7976e1621275e3af7099e7e4a3d4478cf8d5d2a27f3bc3e967", + "zh:b516ca74431f3df4c6cf90ddcdb4042c626e026317a33c53f0b445a3d93b720d", + "zh:dc76e4326aec2490c1600d6871a95e78f9050f9ce427c71707ea412a2f2f1a62", + "zh:eac7b63e86c749c7d48f527671c7aee5b4e26c10be6ad7232d6860167f99dbb0", ] } diff --git a/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/terraform/main.tf b/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/terraform/main.tf index 0d9ac14..e6e0d3e 100644 --- a/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/terraform/main.tf +++ b/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/terraform/main.tf @@ -3,15 +3,15 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "=3.117.0" + version = "= 4.57.0" } random = { source = "hashicorp/random" - version = "=3.5.1" + version = "= 3.7.2" } azuread = { source = "hashicorp/azuread" - version = "3.1.0" + version = "= 3.7.0" } } backend "azurerm" { @@ -43,4 +43,4 @@ provider "azuread" { client_id = var.auth_client_id client_secret = var.auth_client_secret tenant_id = var.auth_tenant_id -} \ No newline at end of file +} diff --git a/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/terraform/variables.tf b/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/terraform/variables.tf index 82c3403..871fa39 100644 --- a/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/terraform/variables.tf +++ b/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/terraform/variables.tf @@ -26,6 +26,17 @@ variable "image_gallery_id" { type = string default = "" } +variable "enable_shutdown_schedule" { + type = bool + default = false +} +variable "shutdown_time" { + type = string +} +variable "shutdown_timezone" { + type = string + default = "UTC" +} variable "owner_id" { type = string } diff --git a/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/terraform/vm_config.ps1 b/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/terraform/vm_config.ps1 index aa82710..c0afcae 100644 --- a/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/terraform/vm_config.ps1 +++ b/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/terraform/vm_config.ps1 @@ -1,59 +1,19 @@ -Remove-Item -LiteralPath "C:\AzureData" -Force -Recurse $ErrorActionPreference = "Stop" -if( ${SharedStorageAccess} -eq 1 ) -{ - $Command = "net use z: \\${StorageAccountFileHost}\${FileShareName} /u:AZURE\${StorageAccountName} ${StorageAccountKey}" - $Command | Out-File "C:\ProgramData\Start Menu\Programs\StartUp\attach_storage.cmd" -encoding ascii -} - -$PipConfigFolderPath = "C:\ProgramData\pip\" -If(!(Test-Path $PipConfigFolderPath)) -{ - New-Item -ItemType Directory -Force -Path $PipConfigFolderPath -} - -$PipConfigFilePath = $PipConfigFolderPath + "pip.ini" - -$ConfigBody = @" -[global] -index = ${nexus_proxy_url}/repository/pypi/pypi -index-url = ${nexus_proxy_url}/repository/pypi/simple -trusted-host = ${nexus_proxy_url} -"@ - -# We need to write the ini file in UTF8 (No BOM) as pip won't understand Powershell's default encoding (unicode) -$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False -[System.IO.File]::WriteAllLines($PipConfigFilePath, $ConfigBody, $Utf8NoBomEncoding) - -### Anaconda Config -if( ${CondaConfig} -eq 1 ) -{ - conda config --add channels ${nexus_proxy_url}/repository/conda-mirror/main/ --system - conda config --add channels ${nexus_proxy_url}/repository/conda-repo/main/ --system - conda config --remove channels defaults --system - conda config --set channel_alias ${nexus_proxy_url}/repository/conda-mirror/ --system -} - -# Docker proxy config -$DaemonConfig = @" -{ -"registry-mirrors": ["${nexus_proxy_url}:8083"] -} -"@ -$DaemonConfig | Out-File -Encoding Ascii ( New-Item -Path $env:ProgramData\docker\config\daemon.json -Force ) - -# R config -# $RconfigFilePathWindows = C:\Progra~1\R\4.1.2\etc\Rprofile.site -#Add-Content $RconfigFilePathWindows "local({`n r <- getOption(`"repos`")`n r[`"Nexus`"] <- `"${nexus_proxy_url}/repository/r-proxy/`"`n options(repos = r)`n})" -# echo "local({`n r <- getOption(`"repos`")`n r[`"Nexus`"] <- `"${nexus_proxy_url}/repository/r-proxy/`"`n options(repos = r)`n})" > $RconfigFilePathWindows -$RConfig = @" -local({ - r <- getOption("repos") - r["Nexus"] <- "${nexus_proxy_url}/repository/r-proxy/" - options(repos = r) -}) - -Sys.setenv(R_LIBCURL_SSL_REVOKE_BEST_EFFORT=TRUE) -"@ -$RConfig | Out-File -Encoding Ascii ( New-Item -Path $Env:C:\Software\R\etc\Rprofile.site -Force ) +# --------------------------------------------------------------------------- +# NOTE: The following are pre-configured in the VM image and are NOT repeated here: +# - pip (C:\ProgramData\pip\pip.ini) → Nexus PyPI proxy +# - conda (system .condarc) → Nexus conda proxy +# - R (Rprofile.site, wininet method) → Nexus R proxy +# - Anaconda PATH set at Machine scope → C:\tools\Anaconda3 +# - Docker not installed (WSL2/Hyper-V incompatible with Windows Server 2019) +# --------------------------------------------------------------------------- + +# Remove Azure provisioning artefact +Remove-Item -LiteralPath "C:\AzureData" -Force -Recurse -ErrorAction SilentlyContinue + +# Shared storage — write a startup script that maps the share at logon +if ( ${SharedStorageAccess} -eq 1 ) { + $Command = "net use z: \\${StorageAccountFileHost}\${FileShareName} /u:AZURE\${StorageAccountName} ${StorageAccountKey}" + $Command | Out-File "C:\ProgramData\Start Menu\Programs\StartUp\attach_storage.cmd" -Encoding Ascii +} \ No newline at end of file diff --git a/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/terraform/windowsvm.tf b/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/terraform/windowsvm.tf index 21d76f9..6a9e90c 100644 --- a/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/terraform/windowsvm.tf +++ b/templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm-ouh2/terraform/windowsvm.tf @@ -128,4 +128,17 @@ resource "azurerm_key_vault_secret" "windowsvm_password" { tags = local.tre_user_resources_tags lifecycle { ignore_changes = [tags] } +} + +resource "azurerm_dev_test_global_vm_shutdown_schedule" "shutdown_schedule" { + count = var.enable_shutdown_schedule ? 1 : 0 + + location = data.azurerm_resource_group.ws.location + virtual_machine_id = azurerm_windows_virtual_machine.windowsvm.id + daily_recurrence_time = var.shutdown_time + timezone = var.shutdown_timezone + enabled = var.enable_shutdown_schedule + notification_settings { + enabled = false + } } \ No newline at end of file diff --git a/vm-images/README.md b/vm-images/README.md index ac523c4..1dd7b52 100644 --- a/vm-images/README.md +++ b/vm-images/README.md @@ -1,5 +1,7 @@ # Custom Virtual Machine Images +**This process is being replaced - ReadMe will remain but may be archived.** + This folder contains files that deploy the infrastructre used to create and publish custom virtual machine (VM) images in Azure. ## Folder Structure