From 3ffe7f55aa7a52430fdbae0609c4ca28a5cef3ce Mon Sep 17 00:00:00 2001 From: Muhammad Atif Ali Date: Wed, 20 Dec 2023 08:24:45 +0300 Subject: [PATCH] feat(examples/templates): add aws vm devcontainer template (#11248) * feat(examples/templates): add aws vm devcontainer template * Create README.md * add code-server * fix code-server * `make fmt` * Add files via upload * Update README.md * fix typo and persist workspace * always land in the repo directory --- examples/templates/aws-devcontainer/README.md | 94 +++++++++ .../aws-devcontainer/architecture.svg | 8 + examples/templates/aws-devcontainer/main.tf | 181 ++++++++++++++++++ 3 files changed, 283 insertions(+) create mode 100644 examples/templates/aws-devcontainer/README.md create mode 100644 examples/templates/aws-devcontainer/architecture.svg create mode 100644 examples/templates/aws-devcontainer/main.tf diff --git a/examples/templates/aws-devcontainer/README.md b/examples/templates/aws-devcontainer/README.md new file mode 100644 index 0000000000..7267f62928 --- /dev/null +++ b/examples/templates/aws-devcontainer/README.md @@ -0,0 +1,94 @@ +--- +display_name: AWS EC2 (Devcontainer) +description: Provision AWS EC2 VMs with a devcontainer as Coder workspaces +icon: ../../../site/static/icon/aws.png +maintainer_github: coder +verified: true +tags: [vm, linux, aws, persistent, devcontainer] +--- + +# Remote Development on AWS EC2 VMs using a Devcontainer + +Provision AWS EC2 VMs as [Coder workspaces](https://coder.com/docs/v2/latest) with this example template. +![Architecture Diagram](./architecture.svg) + + + +## Prerequisites + +### Authentication + +By default, this template authenticates to AWS using the provider's default [authentication methods](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication-and-configuration). + +The simplest way (without making changes to the template) is via environment variables (e.g. `AWS_ACCESS_KEY_ID`) or a [credentials file](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html#cli-configure-files-format). If you are running Coder on a VM, this file must be in `/home/coder/aws/credentials`. + +To use another [authentication method](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication), edit the template. + +## Required permissions / policy + +The following sample policy allows Coder to create EC2 instances and modify +instances provisioned by Coder: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "VisualEditor0", + "Effect": "Allow", + "Action": [ + "ec2:GetDefaultCreditSpecification", + "ec2:DescribeIamInstanceProfileAssociations", + "ec2:DescribeTags", + "ec2:DescribeInstances", + "ec2:DescribeInstanceTypes", + "ec2:CreateTags", + "ec2:RunInstances", + "ec2:DescribeInstanceCreditSpecifications", + "ec2:DescribeImages", + "ec2:ModifyDefaultCreditSpecification", + "ec2:DescribeVolumes" + ], + "Resource": "*" + }, + { + "Sid": "CoderResources", + "Effect": "Allow", + "Action": [ + "ec2:DescribeInstanceAttribute", + "ec2:UnmonitorInstances", + "ec2:TerminateInstances", + "ec2:StartInstances", + "ec2:StopInstances", + "ec2:DeleteTags", + "ec2:MonitorInstances", + "ec2:CreateTags", + "ec2:RunInstances", + "ec2:ModifyInstanceAttribute", + "ec2:ModifyInstanceCreditSpecification" + ], + "Resource": "arn:aws:ec2:*:*:instance/*", + "Condition": { + "StringEquals": { + "aws:ResourceTag/Coder_Provisioned": "true" + } + } + } + ] +} +``` + +## Architecture + +This template provisions the following resources: + +- AWS Instance + +Coder uses `aws_ec2_instance_state` to start and stop the VM. This example template is fully persistent, meaning the full filesystem is preserved when the workspace restarts. See this [community example](https://github.com/bpmct/coder-templates/tree/main/aws-linux-ephemeral) of an ephemeral AWS instance. + +> **Note** +> This template is designed to be a starting point! Edit the Terraform to extend the template to support your use case. + +## code-server + +`code-server` is installed via the [`code-server`](https://registry.coder.com/modules/code-server) registry module. For a list of all modules and templates pplease check [Coder Registry](https://registry.coder.com). diff --git a/examples/templates/aws-devcontainer/architecture.svg b/examples/templates/aws-devcontainer/architecture.svg new file mode 100644 index 0000000000..737db7402e --- /dev/null +++ b/examples/templates/aws-devcontainer/architecture.svg @@ -0,0 +1,8 @@ +AWSAWSHostingHostingVirtual MachineVirtual MachineLinux HardwareLinux HardwareCoder WorkspaceCoder WorkspaceDevcontainerDevcontainerenvbuilder created filesytemenvbuilder created filesytemA Clone of your repoA Clone of your repoSource codeSource codeLanguagesLanguagesPython. Go, etcPython. Go, etcToolingToolingExtensions, linting, formatting, etcExtensions, linting, formatting, etcCPUsCPUsDisk StorageDisk StorageCode EditorCode EditorVS Code DesktopVS Code DesktopLocal InstallationLocal InstallationVS Code DesktopVS Code DesktopLocal InstallationLocal Installationcode-servercode-serverA web IDEA web IDEJetBrains GatewayJetBrains GatewayLocal InstallationLocal InstallationCommand LineCommand LineSSH via Coder CLISSH via Coder CLI \ No newline at end of file diff --git a/examples/templates/aws-devcontainer/main.tf b/examples/templates/aws-devcontainer/main.tf new file mode 100644 index 0000000000..74d380723a --- /dev/null +++ b/examples/templates/aws-devcontainer/main.tf @@ -0,0 +1,181 @@ +terraform { + required_providers { + coder = { + source = "coder/coder" + } + aws = { + source = "hashicorp/aws" + } + } +} + +module "aws_region" { + source = "https://registry.coder.com/modules/aws-region" + default = "us-east-1" +} + +data "coder_parameter" "instance_type" { + name = "instance_type" + display_name = "Instance type" + description = "What instance type should your workspace use?" + default = "t3.micro" + mutable = false + option { + name = "2 vCPU, 1 GiB RAM" + value = "t3.micro" + } + option { + name = "2 vCPU, 2 GiB RAM" + value = "t3.small" + } + option { + name = "2 vCPU, 4 GiB RAM" + value = "t3.medium" + } + option { + name = "2 vCPU, 8 GiB RAM" + value = "t3.large" + } + option { + name = "4 vCPU, 16 GiB RAM" + value = "t3.xlarge" + } + option { + name = "8 vCPU, 32 GiB RAM" + value = "t3.2xlarge" + } +} + +provider "aws" { + region = module.aws_region.value +} + +data "coder_workspace" "me" { +} + +data "aws_ami" "ubuntu" { + most_recent = true + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] + } + filter { + name = "virtualization-type" + values = ["hvm"] + } + owners = ["099720109477"] # Canonical +} + +data "coder_parameter" "repo_url" { + name = "repo_url" + display_name = "Repository URL" + default = "https://github.com/coder/envbuilder-starter-devcontainer" + description = "Repository URL" + mutable = true +} + +resource "coder_agent" "dev" { + count = data.coder_workspace.me.start_count + arch = "amd64" + auth = "token" + os = "linux" + dir = "/workspaces/${trimsuffix(basename(data.coder_parameter.repo_url.value), ".git")}" + connection_timeout = 0 + + metadata { + key = "cpu" + display_name = "CPU Usage" + interval = 5 + timeout = 5 + script = "coder stat cpu" + } + metadata { + key = "memory" + display_name = "Memory Usage" + interval = 5 + timeout = 5 + script = "coder stat mem" + } +} + +module "code-server" { + count = data.coder_workspace.me.start_count + source = "https://registry.coder.com/modules/code-server" + agent_id = coder_agent.dev[0].id +} + +locals { + linux_user = "coder" + user_data = <<-EOT + Content-Type: multipart/mixed; boundary="//" + MIME-Version: 1.0 + + --// + Content-Type: text/cloud-config; charset="us-ascii" + MIME-Version: 1.0 + Content-Transfer-Encoding: 7bit + Content-Disposition: attachment; filename="cloud-config.txt" + + #cloud-config + cloud_final_modules: + - [scripts-user, always] + hostname: ${lower(data.coder_workspace.me.name)} + users: + - name: ${local.linux_user} + sudo: ALL=(ALL) NOPASSWD:ALL + shell: /bin/bash + + --// + Content-Type: text/x-shellscript; charset="us-ascii" + MIME-Version: 1.0 + Content-Transfer-Encoding: 7bit + Content-Disposition: attachment; filename="userdata.txt" + + #!/bin/bash + # Install Docker + if ! command -v docker &> /dev/null + then + echo "Docker not found, installing..." + curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh 2>&1 >/dev/null + usermod -aG docker ${local.linux_user} + newgrp docker + else + echo "Docker is already installed." + fi + + # Start envbuilder + docker run --rm \ + -v /home/${local.linux_user}/envbuilder:/workspaces \ + -e CODER_AGENT_TOKEN="${try(coder_agent.dev[0].token, "")}" \ + -e CODER_AGENT_URL="${data.coder_workspace.me.access_url}" \ + -e GIT_URL="${data.coder_parameter.repo_url.value}" \ + -e INIT_SCRIPT="echo ${base64encode(try(coder_agent.dev[0].init_script, ""))} | base64 -d | sh" \ + -e FALLBACK_IMAGE="codercom/enterprise-base:ubuntu" \ + ghcr.io/coder/envbuilder + --//-- + EOT +} + +resource "aws_instance" "vm" { + ami = data.aws_ami.ubuntu.id + availability_zone = "${module.aws_region.value}a" + instance_type = data.coder_parameter.instance_type.value + root_block_device { + volume_size = 30 + } + + user_data = local.user_data + tags = { + Name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}" + # Required if you are using our example policy, see template README + Coder_Provisioned = "true" + } + lifecycle { + ignore_changes = [ami] + } +} + +resource "aws_ec2_instance_state" "vm" { + instance_id = aws_instance.vm.id + state = data.coder_workspace.me.transition == "start" ? "running" : "stopped" +}