From e1edc61160ae9c2600597e82cd05d48b623dc552 Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Wed, 27 Jul 2022 16:25:11 -0700 Subject: [PATCH 1/3] azure-linux example template Signed-off-by: Spike Curtis --- examples/templates/azure-linux/README.md | 17 ++ .../azure-linux/cloud-config.yaml.tftpl | 58 +++++ examples/templates/azure-linux/main.tf | 227 ++++++++++++++++++ 3 files changed, 302 insertions(+) create mode 100644 examples/templates/azure-linux/README.md create mode 100644 examples/templates/azure-linux/cloud-config.yaml.tftpl create mode 100644 examples/templates/azure-linux/main.tf diff --git a/examples/templates/azure-linux/README.md b/examples/templates/azure-linux/README.md new file mode 100644 index 0000000000000..ce84b5e9a2e59 --- /dev/null +++ b/examples/templates/azure-linux/README.md @@ -0,0 +1,17 @@ +--- +name: Develop in Linux on Azure +description: Get started with Linux development on Microsoft Azure. +tags: [cloud, azure, linux] +--- + +# azure-linux + +To get started, run `coder templates init`. When prompted, select this template. +Follow the on-screen instructions to proceed. + +## Authentication + +This template assumes that coderd is run in an environment that is authenticated +with Azure. For example, run `az login` then `az account set --subscription=` +to import credentials on the system and user running coderd. For other ways to +authenticate [consult the Terraform docs](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs#authenticating-to-azure). diff --git a/examples/templates/azure-linux/cloud-config.yaml.tftpl b/examples/templates/azure-linux/cloud-config.yaml.tftpl new file mode 100644 index 0000000000000..7c232c4bc1757 --- /dev/null +++ b/examples/templates/azure-linux/cloud-config.yaml.tftpl @@ -0,0 +1,58 @@ +#cloud-config +cloud_final_modules: +- [scripts-user, always] +device_aliases: + # this is a little fragile because the data volume isn't always sdc when the workspace is stopped + # and started. But, it appears that it is always sdc when the workspace is *first* created. + # This allows us to format and label the volume, and then we rely on the label to mount on + # subsequent workspace builds. + homedir: /dev/sdc +disk_setup: + homedir: + table_type: gpt + layout: true +fs_setup: + - label: coder_home + filesystem: ext4 + device: homedir.1 +mounts: + - ["LABEL=coder_home", "/home/${username}"] +hostname: ${hostname} +users: + - name: ${username} + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + groups: sudo + shell: /bin/bash +packages: + - git +write_files: + - path: /opt/coder/init + permissions: "0755" + encoding: b64 + content: ${init_script} + - path: /etc/systemd/system/coder-agent.service + permissions: "0644" + content: | + [Unit] + Description=Coder Agent + After=network-online.target + Wants=network-online.target + + [Service] + User=${username} + ExecStart=/opt/coder/init + Environment=CODER_AGENT_TOKEN=${coder_agent_token} + Restart=always + RestartSec=10 + TimeoutStopSec=90 + KillMode=process + + OOMScoreAdjust=-900 + SyslogIdentifier=coder-agent + + [Install] + WantedBy=multi-user.target +runcmd: + - chown ${username}:${username} /home/${username} + - systemctl enable coder-agent + - systemctl start coder-agent diff --git a/examples/templates/azure-linux/main.tf b/examples/templates/azure-linux/main.tf new file mode 100644 index 0000000000000..99c65f3b82ccf --- /dev/null +++ b/examples/templates/azure-linux/main.tf @@ -0,0 +1,227 @@ +terraform { + required_providers { + coder = { + source = "coder/coder" + version = "0.4.3" + } + azurerm = { + source = "hashicorp/azurerm" + version = "=3.0.0" + } + } +} + +variable "location" { + description = "What location should your workspace live in?" + default = "eastus" + validation { + condition = contains([ + "eastus", + "southcentralus", + "westus2", + "australiaeast", + "southeastasia", + "northeurope", + "westeurope", + "centralindia", + "eastasia", + "japaneast", + "brazilsouth", + "asia", + "asiapacific", + "australia", + "brazil", + "india", + "japan", + "southafrica", + "switzerland", + "uae", + ], var.location) + error_message = "Invalid location!" + } +} + +variable "instance_type" { + description = "What instance type should your workspace use?" + default = "Standard_B4ms" + validation { + condition = contains([ + "Standard_B1ms", + "Standard_B2ms", + "Standard_B4ms", + "Standard_B8ms", + "Standard_B12ms", + "Standard_B16ms", + "Standard_D2as_v5", + "Standard_D4as_v5", + "Standard_D8as_v5", + "Standard_D16as_v5", + "Standard_D32as_v5", + ], var.instance_type) + error_message = "Invalid instance type!" + } +} + +variable "home_size" { + type = number + description = "How large would you like your home volume to be (in GB)?" + default = 20 + validation { + condition = var.home_size >= 1 + error_message = "Value must be greater than or equal to 1." + } +} + +provider "azurerm" { + features {} +} + +data "coder_workspace" "me" { +} + +resource "coder_agent" "main" { + arch = "amd64" + os = "linux" +} + +locals { + prefix = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}" + + userdata = templatefile("cloud-config.yaml.tftpl", { + username = lower(substr(data.coder_workspace.me.owner, 0, 32)) + init_script = base64encode(coder_agent.main.init_script) + coder_agent_token = coder_agent.main.token + hostname = lower(data.coder_workspace.me.name) + }) +} + +resource "azurerm_resource_group" "main" { + name = "${local.prefix}-resources" + location = var.location + + tags = { + Name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}" + Coder_Provisioned = "true" + } +} + +// Uncomment here and in the azurerm_network_interface resource to obtain a public IP +#resource "azurerm_public_ip" "main" { +# name = "publicip" +# resource_group_name = azurerm_resource_group.main.name +# location = azurerm_resource_group.main.location +# allocation_method = "Static" +# +# tags = { +# Name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}" +# Coder_Provisioned = "true" +# } +#} + +resource "azurerm_virtual_network" "main" { + name = "network" + address_space = ["10.0.0.0/24"] + location = azurerm_resource_group.main.location + resource_group_name = azurerm_resource_group.main.name + + tags = { + Name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}" + Coder_Provisioned = "true" + } +} + +resource "azurerm_subnet" "internal" { + name = "internal" + resource_group_name = azurerm_resource_group.main.name + virtual_network_name = azurerm_virtual_network.main.name + address_prefixes = ["10.0.0.0/29"] +} + +resource "azurerm_network_interface" "main" { + name = "nic" + resource_group_name = azurerm_resource_group.main.name + location = azurerm_resource_group.main.location + + ip_configuration { + name = "internal" + subnet_id = azurerm_subnet.internal.id + private_ip_address_allocation = "Dynamic" + // Uncomment for public IP address as well as azurerm_public_ip resource above + //public_ip_address_id = azurerm_public_ip.main.id + } + + tags = { + Name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}" + Coder_Provisioned = "true" + } +} + +resource "azurerm_managed_disk" "home" { + create_option = "Empty" + location = azurerm_resource_group.main.location + name = "home" + resource_group_name = azurerm_resource_group.main.name + storage_account_type = "StandardSSD_LRS" + disk_size_gb = var.home_size +} + +// azurerm requires an SSH key (or password) for an admin user or it won't start a VM. However, +// cloud-init overwrites this anyway, so we'll just use a dummy SSH key. +resource "tls_private_key" "dummy" { + algorithm = "RSA" + rsa_bits = 4096 +} + +resource "azurerm_virtual_machine" "main" { + count = data.coder_workspace.me.transition == "start" ? 1 : 0 + name = "vm" + resource_group_name = azurerm_resource_group.main.name + location = azurerm_resource_group.main.location + vm_size = var.instance_type + network_interface_ids = [ + azurerm_network_interface.main.id, + ] + + os_profile { + admin_username = "adminuser" + computer_name = data.coder_workspace.me.name + custom_data = local.userdata + } + + os_profile_linux_config { + disable_password_authentication = true + ssh_keys { + key_data = tls_private_key.dummy.public_key_openssh + path = "/home/adminuser/.ssh/authorized_keys" + } + } + + storage_image_reference { + publisher = "Canonical" + offer = "0001-com-ubuntu-server-focal" + sku = "20_04-lts-gen2" + version = "latest" + } + + storage_os_disk { + managed_disk_type = "StandardSSD_LRS" + caching = "ReadWrite" + create_option = "FromImage" + name = "os" + } + delete_os_disk_on_termination = true + + storage_data_disk { + create_option = "Attach" + lun = 10 + name = "home" + caching = "ReadWrite" + managed_disk_id = azurerm_managed_disk.home.id + disk_size_gb = var.home_size + } + + tags = { + Name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}" + Coder_Provisioned = "true" + } +} From b065bfd9df4dde257d0edb798369eccfd9561899 Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Mon, 8 Aug 2022 13:32:39 -0700 Subject: [PATCH 2/3] Use azurerm_linux_virtual_machine and wait for attachment Signed-off-by: Spike Curtis --- .../azure-linux/cloud-config.yaml.tftpl | 9 ++- examples/templates/azure-linux/main.tf | 62 +++++++------------ 2 files changed, 27 insertions(+), 44 deletions(-) diff --git a/examples/templates/azure-linux/cloud-config.yaml.tftpl b/examples/templates/azure-linux/cloud-config.yaml.tftpl index 7c232c4bc1757..d4895e9feae30 100644 --- a/examples/templates/azure-linux/cloud-config.yaml.tftpl +++ b/examples/templates/azure-linux/cloud-config.yaml.tftpl @@ -1,12 +1,11 @@ #cloud-config cloud_final_modules: - [scripts-user, always] +bootcmd: + # work around https://github.com/hashicorp/terraform-provider-azurerm/issues/6117 + - until [ -e /dev/disk/azure/scsi1/lun10 ]; do sleep 1; done device_aliases: - # this is a little fragile because the data volume isn't always sdc when the workspace is stopped - # and started. But, it appears that it is always sdc when the workspace is *first* created. - # This allows us to format and label the volume, and then we rely on the label to mount on - # subsequent workspace builds. - homedir: /dev/sdc + homedir: /dev/disk/azure/scsi1/lun10 disk_setup: homedir: table_type: gpt diff --git a/examples/templates/azure-linux/main.tf b/examples/templates/azure-linux/main.tf index 99c65f3b82ccf..27c977a0c9695 100644 --- a/examples/templates/azure-linux/main.tf +++ b/examples/templates/azure-linux/main.tf @@ -100,7 +100,6 @@ resource "azurerm_resource_group" "main" { location = var.location tags = { - Name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}" Coder_Provisioned = "true" } } @@ -113,7 +112,6 @@ resource "azurerm_resource_group" "main" { # allocation_method = "Static" # # tags = { -# Name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}" # Coder_Provisioned = "true" # } #} @@ -125,7 +123,6 @@ resource "azurerm_virtual_network" "main" { resource_group_name = azurerm_resource_group.main.name tags = { - Name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}" Coder_Provisioned = "true" } } @@ -151,7 +148,6 @@ resource "azurerm_network_interface" "main" { } tags = { - Name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}" Coder_Provisioned = "true" } } @@ -172,56 +168,44 @@ resource "tls_private_key" "dummy" { rsa_bits = 4096 } -resource "azurerm_virtual_machine" "main" { +resource "azurerm_linux_virtual_machine" "main" { count = data.coder_workspace.me.transition == "start" ? 1 : 0 name = "vm" resource_group_name = azurerm_resource_group.main.name location = azurerm_resource_group.main.location - vm_size = var.instance_type + size = var.instance_type + // cloud-init overwrites this, so the value here doesn't matter + admin_username = "adminuser" + admin_ssh_key { + public_key = tls_private_key.dummy.public_key_openssh + username = "adminuser" + } + network_interface_ids = [ azurerm_network_interface.main.id, ] - - os_profile { - admin_username = "adminuser" - computer_name = data.coder_workspace.me.name - custom_data = local.userdata - } - - os_profile_linux_config { - disable_password_authentication = true - ssh_keys { - key_data = tls_private_key.dummy.public_key_openssh - path = "/home/adminuser/.ssh/authorized_keys" - } + computer_name = lower(data.coder_workspace.me.name) + os_disk { + caching = "ReadWrite" + storage_account_type = "Standard_LRS" } - - storage_image_reference { + source_image_reference { publisher = "Canonical" offer = "0001-com-ubuntu-server-focal" sku = "20_04-lts-gen2" version = "latest" } - - storage_os_disk { - managed_disk_type = "StandardSSD_LRS" - caching = "ReadWrite" - create_option = "FromImage" - name = "os" - } - delete_os_disk_on_termination = true - - storage_data_disk { - create_option = "Attach" - lun = 10 - name = "home" - caching = "ReadWrite" - managed_disk_id = azurerm_managed_disk.home.id - disk_size_gb = var.home_size - } + user_data = base64encode(local.userdata) tags = { - Name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}" Coder_Provisioned = "true" } } + +resource "azurerm_virtual_machine_data_disk_attachment" "home" { + count = data.coder_workspace.me.transition == "start" ? 1 : 0 + managed_disk_id = azurerm_managed_disk.home.id + virtual_machine_id = azurerm_linux_virtual_machine.main[0].id + lun = "10" + caching = "ReadWrite" +} From f41d5e2c1d656642821ea7d5755741492b53f5b8 Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Mon, 8 Aug 2022 14:11:59 -0700 Subject: [PATCH 3/3] Use azure-instance-identity Signed-off-by: Spike Curtis --- examples/templates/azure-linux/cloud-config.yaml.tftpl | 1 - examples/templates/azure-linux/main.tf | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/templates/azure-linux/cloud-config.yaml.tftpl b/examples/templates/azure-linux/cloud-config.yaml.tftpl index d4895e9feae30..75006778331b8 100644 --- a/examples/templates/azure-linux/cloud-config.yaml.tftpl +++ b/examples/templates/azure-linux/cloud-config.yaml.tftpl @@ -40,7 +40,6 @@ write_files: [Service] User=${username} ExecStart=/opt/coder/init - Environment=CODER_AGENT_TOKEN=${coder_agent_token} Restart=always RestartSec=10 TimeoutStopSec=90 diff --git a/examples/templates/azure-linux/main.tf b/examples/templates/azure-linux/main.tf index 27c977a0c9695..2406d6a5901bb 100644 --- a/examples/templates/azure-linux/main.tf +++ b/examples/templates/azure-linux/main.tf @@ -82,6 +82,7 @@ data "coder_workspace" "me" { resource "coder_agent" "main" { arch = "amd64" os = "linux" + auth = "azure-instance-identity" } locals { @@ -90,7 +91,6 @@ locals { userdata = templatefile("cloud-config.yaml.tftpl", { username = lower(substr(data.coder_workspace.me.owner, 0, 32)) init_script = base64encode(coder_agent.main.init_script) - coder_agent_token = coder_agent.main.token hostname = lower(data.coder_workspace.me.name) }) }