-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add:
terraform
google compute engine vm setup (#2482)
* add: `terraform` gitignore file * add: `gce` vm terraform module * add: convenience `scripts` * add: `readme` with instructions
- Loading branch information
Showing
7 changed files
with
539 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# Local .terraform directories | ||
**/.terraform/* | ||
|
||
# .tfstate files | ||
*.tfstate | ||
*.tfstate.* | ||
|
||
# Crash log files | ||
crash.log | ||
crash.*.log | ||
|
||
# Exclude all .tfvars files, which are likely to contain sensitive data, such as | ||
# password, private keys, and other secrets. These should not be part of version | ||
# control as they are data points which are potentially sensitive and subject | ||
# to change depending on the environment. | ||
*.tfvars | ||
*.tfvars.json | ||
|
||
# Ignore override files as they are usually used to override resources locally and so | ||
# are not checked in | ||
override.tf | ||
override.tf.json | ||
*_override.tf | ||
*_override.tf.json | ||
|
||
# Ignore transient lock info files created by terraform apply | ||
.terraform.tfstate.lock.info | ||
|
||
# Include override files you do wish to add to version control using negated pattern | ||
# !example_override.tf | ||
|
||
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan | ||
# example: *tfplan* | ||
|
||
# Ignore CLI configuration files | ||
.terraformrc | ||
terraform.rc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# gce-dev-vm | ||
|
||
Sets up a GCE vm suitable for remote development. | ||
|
||
# usage | ||
|
||
> [!WARNING] | ||
> Make sure you are logged in to the correct GCP project before running the | ||
> following commands. | ||
Create a `terraform.tfvars` file with the following content. To pick your zone | ||
and region, the rule of thumb is to pick the region closest to you. Use this | ||
[link](https://googlecloudplatform.github.io/region-picker/) to find the region | ||
and zone closest to you. | ||
|
||
```hcl | ||
project_id = "your-project-id" | ||
credentials_file = "/path/to/application_default_credentials.json" | ||
public_key_path = "/path/to/ssh_key.pub" | ||
ssh_user = "user" | ||
zone = "your-zone1" | ||
region = "your-zone1-a" | ||
``` | ||
|
||
Now, initialize the terraform module, and plan the changes. | ||
|
||
```bash | ||
terraform init | ||
terraform plan | ||
``` | ||
|
||
Check that everything looks good, and apply the changes. | ||
|
||
```bash | ||
terraform apply | ||
``` | ||
|
||
After the apply is complete, you should see the external IP of the VM in the | ||
output. You can now ssh into the VM using the following command. | ||
|
||
```bash | ||
ssh -i /path/to/ssh_key user@external-ip | ||
``` | ||
|
||
# features | ||
|
||
Installs development tools, `python3.12` from source and `nvm` for managing | ||
node. | ||
|
||
## post-install | ||
|
||
After the VM is created, you should change the password for the user. You can | ||
run the following command to change the password. | ||
|
||
```bash | ||
sudo passwd user | ||
``` | ||
|
||
You should also add ssh keys to the newly created VM. You can either copy your | ||
current ssh keys to the VM or generate new ones. To copy your current ssh keys | ||
to the VM, you can run the following command. | ||
|
||
```bash | ||
ssh-copy-id -i /path/to/ssh_key user@external-ip | ||
``` | ||
|
||
To generate new ssh keys, you can run the following command. | ||
|
||
```bash | ||
ssh-keygen -t rsa -b 4096 -C "user@email.com" | ||
``` | ||
|
||
After generating the keys, follow | ||
[GitHub's guide](https://docs.github.com/en/github/authenticating-to-github/adding-a-new-ssh-key-to-your-github-account) | ||
to add the ssh key to your GitHub account. | ||
|
||
You should also set `GPG` keys for signing commits. You can follow | ||
[GitHub's guide](https://docs.github.com/en/github/authenticating-to-github/managing-commit-signature-verification) | ||
as well. | ||
|
||
## convenience scripts | ||
|
||
We also provide [`scrips/connect.sh`](./scripts/connect.sh), which you can use | ||
to automatically add the machine `ip` to your `~/.ssh/config` file. This will | ||
allow you to ssh into the machine using the `ssh user@name:project:region` | ||
command. | ||
|
||
With this script, we can create two aliases in our `~/.bashrc` file, to start | ||
and stop the VM. These will add and remove the machine `ip` from the | ||
`~/.ssh/config` file respectively, as well as start and stop the VM, to save | ||
costs. | ||
|
||
```bash | ||
alias osostart="bash /path/to/gce-dev-vm/scripts/connect.sh start --project $PROJECT --instance $NAME -z $ZONE -u $USER" | ||
alias osostop="bash /path/to/gce-dev-vm/scripts/connect.sh stop --project $PROJECT --instance $NAME --zone $ZONE" | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
terraform { | ||
required_providers { | ||
google = { | ||
source = "hashicorp/google" | ||
} | ||
} | ||
} | ||
|
||
provider "google" { | ||
project = var.project_id | ||
credentials = file(var.credentials_file) | ||
region = var.region | ||
zone = var.zone | ||
} | ||
|
||
resource "google_service_account" "iam_service_account" { | ||
account_id = var.service_account_id | ||
display_name = var.service_account_display_name | ||
} | ||
|
||
resource "google_project_iam_member" "bigquery_admin" { | ||
project = var.project_id | ||
role = var.bigquery_admin_role | ||
member = "serviceAccount:${google_service_account.iam_service_account.email}" | ||
} | ||
|
||
resource "google_project_iam_member" "iam_service_account_admin" { | ||
project = var.project_id | ||
role = var.iam_service_account_admin_role | ||
member = "serviceAccount:${google_service_account.iam_service_account.email}" | ||
} | ||
|
||
resource "google_project_iam_member" "cloud_platform" { | ||
project = var.project_id | ||
role = var.cloud_platform_role | ||
member = "serviceAccount:${google_service_account.iam_service_account.email}" | ||
} | ||
|
||
resource "google_compute_network" "dev_network" { | ||
name = var.network_name | ||
} | ||
|
||
resource "google_compute_firewall" "ssh_firewall" { | ||
name = var.firewall_name | ||
network = google_compute_network.dev_network.name | ||
|
||
allow { | ||
protocol = "tcp" | ||
ports = var.firewall_ports | ||
} | ||
|
||
target_tags = var.firewall_target_tags | ||
source_ranges = var.firewall_source_ranges | ||
} | ||
|
||
resource "google_compute_instance" "dev_vm" { | ||
name = var.machine_name | ||
machine_type = var.machine_type | ||
hostname = var.machine_hostname | ||
allow_stopping_for_update = true | ||
tags = var.vm_tags | ||
service_account { | ||
email = google_service_account.iam_service_account.email | ||
scopes = var.service_account_scopes | ||
} | ||
|
||
boot_disk { | ||
device_name = var.boot_disk_device_name | ||
|
||
initialize_params { | ||
image = var.image | ||
type = var.disk_type | ||
size = var.disk_size | ||
} | ||
} | ||
|
||
scheduling { | ||
preemptible = var.preemptible | ||
} | ||
|
||
network_interface { | ||
network = google_compute_network.dev_network.id | ||
access_config { | ||
|
||
} | ||
} | ||
|
||
metadata = { | ||
ssh-keys = "${var.ssh_user}:${file(var.public_key_path)}" | ||
startup-script = file(var.startup_script_path) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
output "vm_public_ip" { | ||
description = "Public IP of the VM" | ||
value = google_compute_instance.dev_vm.network_interface.0.access_config.0.nat_ip | ||
} | ||
|
||
output "service_account_email" { | ||
description = "Email address of the IAM service account" | ||
value = google_service_account.iam_service_account.email | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
#!/bin/bash | ||
|
||
ZONE="us-central1-a" | ||
USER="$USER" | ||
|
||
parse_arguments() { | ||
while [[ $# -gt 0 ]]; do | ||
case "$1" in | ||
start | stop) | ||
ACTION="$1" | ||
shift | ||
;; | ||
--project | -p) | ||
PROJECT_ID="$2" | ||
shift 2 | ||
;; | ||
--instance | -i) | ||
INSTANCE_NAME="$2" | ||
shift 2 | ||
;; | ||
--zone | -z) | ||
ZONE="$2" | ||
shift 2 | ||
;; | ||
--user | -u) | ||
USER="$2" | ||
shift 2 | ||
;; | ||
--help | -h) | ||
help | ||
exit 0 | ||
;; | ||
*) | ||
echo "Unknown option: $1" | ||
help | ||
exit 1 | ||
;; | ||
esac | ||
done | ||
|
||
if [[ -z "$ACTION" || -z "$PROJECT_ID" || -z "$INSTANCE_NAME" ]]; then | ||
echo "Error: Missing required arguments." | ||
help | ||
exit 1 | ||
fi | ||
} | ||
|
||
start_instance() { | ||
config_file="$HOME/.ssh/config" | ||
|
||
[[ ! -f "$config_file" ]] && touch "$config_file" | ||
|
||
gcloud compute instances start "$INSTANCE_NAME" --zone "$ZONE" --project "$PROJECT_ID" | ||
|
||
external_ip=$( | ||
gcloud compute instances describe "$INSTANCE_NAME" --zone "$ZONE" --project "$PROJECT_ID" | grep natIP | awk '{print $2}' | ||
) | ||
|
||
if [[ ! -f "$HOME/.ssh/google_compute_engine" ]]; then | ||
echo "No SSH key found. Please select one from the list below:" | ||
select key in $(ls $HOME/.ssh/*.pub); do | ||
echo "Using $key, creating symlink to $HOME/.ssh/google_compute_engine" | ||
ln -s "$key" $HOME/.ssh/google_compute_engine | ||
break | ||
done | ||
fi | ||
|
||
if grep -q "Host $INSTANCE_NAME:$PROJECT_ID:$ZONE" "$config_file"; then | ||
sed -i "" "s/HostName .*/HostName $external_ip/" "$config_file" | ||
else | ||
[[ $(od -An -tc -N1 "$config_file") != $'\n' ]] && echo >>"$config_file" | ||
echo "Host $INSTANCE_NAME:$PROJECT_ID:$ZONE" >>"$config_file" | ||
echo " HostName $external_ip" >>"$config_file" | ||
echo " User $USER" >>"$config_file" | ||
echo " IdentityFile $HOME/.ssh/google_compute_engine" >>"$config_file" | ||
fi | ||
} | ||
|
||
stop_instance() { | ||
config_file="$HOME/.ssh/config" | ||
|
||
if grep -q "Host $INSTANCE_NAME:$PROJECT_ID:$ZONE" "$config_file"; then | ||
sed -i "" "/Host $INSTANCE_NAME:$PROJECT_ID:$ZONE/,+3d" "$config_file" | ||
sed -i "" -e :a -e '/^\n*$/{$d;N;ba' -e '}' "$config_file" | ||
fi | ||
|
||
gcloud compute instances stop "$INSTANCE_NAME" --zone "$ZONE" --project "$PROJECT_ID" | ||
} | ||
|
||
help() { | ||
echo "Usage: $0 start|stop --project <project_id> --instance <instance_name> [--zone <zone>] [--user <user>]" | ||
echo | ||
echo "Options:" | ||
echo " start|stop Action to perform (start or stop the instance)" | ||
echo " --project, -p Google Cloud project ID" | ||
echo " --instance, -i Name of the compute instance" | ||
echo " --zone, -z Compute instance zone (default: us-central1-a)" | ||
echo " --user, -u SSH user (default: current system user)" | ||
echo " --help, -h Display this help message" | ||
} | ||
|
||
parse_arguments "$@" | ||
|
||
if [[ "$ACTION" == "start" ]]; then | ||
start_instance | ||
elif [[ "$ACTION" == "stop" ]]; then | ||
stop_instance | ||
else | ||
help | ||
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
#!/bin/bash | ||
|
||
sudo apt-get update | ||
sudo apt install -y wget \ | ||
build-essential \ | ||
libssl-dev \ | ||
zlib1g-dev \ | ||
libncurses5-dev \ | ||
libgdbm-dev \ | ||
libnss3-dev \ | ||
libreadline-dev \ | ||
libffi-dev \ | ||
curl \ | ||
libbz2-dev \ | ||
git \ | ||
pkg-config | ||
|
||
cd /tmp/ | ||
wget https://www.python.org/ftp/python/3.12.0/Python-3.12.0.tgz | ||
tar -xf Python-3.12.0.tgz | ||
cd Python-3.12.0 | ||
|
||
./configure --enable-optimizations | ||
make -j $(nproc) | ||
sudo make altinstall | ||
|
||
cd /tmp/ | ||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash | ||
|
||
echo 'export NVM_DIR="$HOME/.nvm"' >>~/.bashrc | ||
echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >>~/.bashrc | ||
echo '[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"' >>~/.bashrc | ||
|
||
. ~/.bashrc | ||
|
||
nvm install node | ||
nvm use node |
Oops, something went wrong.