10# Automating setting up a DigitalOcean Server(s) with Terraform 1/2

Alejo Cain
9 min readJul 4, 2023

--

In the previous weeks we had manually setup our DigitalOcean server using the Digitalocean GUI. And later on used GitHub ACctions to push the image of our front-end to it automated.

HOWEVER….. I messed up and now the droplet can’t use nginx properly, it does not matter how many times I try to rm * or rm “that” or purge it. The system has a corrupt installation that can’t be fully removed or fixed.

“I told you so”

The thought of doing all that work again manually daunted me, what if it happens in the future and I need to fix it asap??

So let’s setup the server again but this time use Terraform.

Terraform is an open-source infrastructure as code (IaC) tool. tldr you can setup all your infrastructure with code and automate the process in case something goes wrong. Or do a infrastructure rollback if the new features don’t work out.

Prerequisites

  • DigitalOcean account that is connected to your credit-card (since we will be making small cost)
  • Your own domain
  • New repository where the terraform code will live

Step 1 Installing Terraform CLI

In your terminal input the following if you are on Ubuntu!

wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform

to check if the installation went well input in your terminal

terraform --version
Terraform v1.5.2
on linux_amd64

Step 2 Setting up Digitalocean provider

Our next step is to make a new file called main.tf in our new repository.

And go to the Terraform documentation where we can copy the code we need for our Digitalocean provider.

terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.0"
}
}
}


# Configure the DigitalOcean Provider
provider "digitalocean" {
}

Save this file and in your terminal do the following commands

terraform init 

The command terraform init initializes the back-end, and that in turn will save the ‘state’. Think about it as git init but instead of initializing a project, you create a Terraform project that will manage your infrastructure. Pretty nifty right?

The response should look like this

terraform init

Initializing the backend...

Initializing provider plugins...
- Finding digitalocean/digitalocean versions matching "~> 2.0"...
- Installing digitalocean/digitalocean v2.28.1...
- Installed digitalocean/digitalocean v2.28.1 (signed by a HashiCorp partner, key ID F82037E524B9C0E8)

Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://www.terraform.io/docs/cli/plugins/signing.html

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

Step 3 Creating a droplet with Terraform resources

Here is the link for digitalocean_droplet in the documentation of terraform, copy and paste the code from there.

# Create a new Web Droplet in the nyc2 region
resource "digitalocean_droplet" "web" {
image = "ubuntu-18-04-x64"
name = "web-1"
region = "nyc2"
size = "s-1vcpu-1gb"
}

Lets make changes that are needed for our drag and drop robot builder.

image needs to be ubuntu 20.04 so it’s backward compatible with ROS 1. To check the available images use this long command found here.

curl -X GET   -H "Content-Type: application/json"   -H "Authorization: Bearer $DIGITALOCEAN_TOKEN"   "https://api.digitalocean.com/v2/images?page=1&per_page=1" | jq

I like to use jqbut you don’t need it I do it, but I still recommend getting used to it. Whenever you need to work with a server and want a bit of overview.

sudo apt install jq 

If you get a error that you can’t be authenticated make sure you have your api token in your environment variables like so. Check this story on how to setup a API key.

export DIGITAL_TOKEN=<insert your digialocean api token>

If you had jq installed and you token properly in your environment variables you will get the following response from the curl command

curl -X GET   -H "Content-Type: application/json"   -H "Authorization: Bearer $DIGITALOCEAN_TOKEN"   "https://api.digitalocean.com/v2/images?page=1&per_page=1"  | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 569 100 569 0 0 1903 0 --:--:-- --:--:-- --:--:-- 1896
{
"images": [
{
"id": 106433672,
"name": "7 x64",
"distribution": "CentOS",
"slug": "centos-7-x64",
"public": true,
"regions": [
"tor1",
"syd1",
"sgp1",
"sfo3",
"sfo2",
"sfo1",
"nyc3",
"nyc2",
"nyc1",
"lon1",
"fra1",
"blr1",
"ams3",
"ams2"
],
"created_at": "2022-04-18T21:32:47Z",
"min_disk_size": 9,
"type": "base",
"size_gigabytes": 0.43,
"description": "CentOS 7 x64",
"tags": [],
"status": "available",
"error_message": ""
}
],
"links": {
"pages": {
"last": "https://api.digitalocean.com/v2/images?page=151&per_page=1",
"next": "https://api.digitalocean.com/v2/images?page=2&per_page=1"
}
},
"meta": {
"total": 151
}
}

To see what Ubuntu images are available we will use the following command

curl -X GET \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
"https://api.digitalocean.com/v2/images?page=1&per_page=200" | jq '.images[].slug' | grep ubuntu

The response should be:

"ubuntu-20-04-x64"
"ubuntu-18-04-x64"
"ubuntu-22-10-x64"
"ubuntu-22-04-x64"

As you can see we are safe to use Ubuntu-20–04-x64 perfect!

I changed the region to my closest data center “ams3” and for the size I have chosen for the smallest configuration you can find them here

curl -X GET   -H "Content-Type: application/json"   -H "Authorization: Bearer $DIGITALOCEAN_TOKEN"   "https://api.digitalocean.com/v2/sizes" | jq '.sizes[] | {name: .slug, price: .price_monthly}'

The response should be filtered on name of the slug and the price monthly.

049    0     0  26434      0 --:--:-- --:--:-- --:--:-- 26434
{
"name": "s-1vcpu-512mb-10gb",
"price": 4
}
{
"name": "s-1vcpu-1gb",
"price": 6
}
{
"name": "s-1vcpu-1gb-amd",
"price": 7
}
{
"name": "s-1vcpu-1gb-intel",
"price": 7
}
{
"name": "s-1vcpu-2gb",
"price": 12
}
{
"name": "s-1vcpu-2gb-amd",
"price": 14
}
{
"name": "s-1vcpu-2gb-intel",
"price": 14
}
{
"name": "s-2vcpu-2gb",
"price": 18
}
{
"name": "s-2vcpu-2gb-amd",
"price": 21
}
{
"name": "s-2vcpu-2gb-intel",
"price": 21
}
{
"name": "s-2vcpu-4gb",
"price": 24
}
{
"name": "s-2vcpu-4gb-amd",
"price": 28
}
{
"name": "s-2vcpu-4gb-intel",
"price": 28
}
{
"name": "c-2",
"price": 42
}
{
"name": "s-4vcpu-8gb",
"price": 48
}
{
"name": "s-4vcpu-8gb-amd",
"price": 56
}
{
"name": "s-4vcpu-8gb-intel",
"price": 56
}
{
"name": "g-2vcpu-8gb",
"price": 63
}
{
"name": "gd-2vcpu-8gb",
"price": 68
}
{
"name": "m-2vcpu-16gb",
"price": 84
}

And since we are cheap we will go for the second cheapest option s-1vcpu-1gb again use the command terraform plan

terraform plan

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
+ create

Terraform will perform the following actions:

# digitalocean_droplet.web will be created
+ resource "digitalocean_droplet" "web" {
+ backups = false
+ created_at = (known after apply)
+ disk = (known after apply)
+ graceful_shutdown = false
+ id = (known after apply)
+ image = "ubuntu-20-04-x64"
+ ipv4_address = (known after apply)
+ ipv4_address_private = (known after apply)
+ ipv6 = false
+ ipv6_address = (known after apply)
+ locked = (known after apply)
+ memory = (known after apply)
+ monitoring = false
+ name = "testing-1"
+ price_hourly = (known after apply)
+ price_monthly = (known after apply)
+ private_networking = (known after apply)
+ region = "ams3"
+ resize_disk = true
+ size = "s-1vcpu-1gb"
+ status = (known after apply)
+ urn = (known after apply)
+ vcpus = (known after apply)
+ volume_ids = (known after apply)
+ vpc_uuid = (known after apply)
}

Plan: 1 to add, 0 to change, 0 to destroy.

As you can see the cli is informing you what will be built in this case 1 droplet will be added.

Now input the command terraform apply and when prompted type “yes” and press ‘enter’.

terraform apply

Terraform used the selected providers to generate the following execution plan. Resource
actions are indicated with the following symbols:
+ create

Terraform will perform the following actions:

# digitalocean_droplet.web will be created
+ resource "digitalocean_droplet" "web" {
+ backups = false
+ created_at = (known after apply)
+ disk = (known after apply)
+ graceful_shutdown = false
+ id = (known after apply)
+ image = "ubuntu-20-04-x64"
+ ipv4_address = (known after apply)
+ ipv4_address_private = (known after apply)
+ ipv6 = false
+ ipv6_address = (known after apply)
+ locked = (known after apply)
+ memory = (known after apply)
+ monitoring = false
+ name = "testing-1"
+ price_hourly = (known after apply)
+ price_monthly = (known after apply)
+ private_networking = (known after apply)
+ region = "ams3"
+ resize_disk = true
+ size = "s-1vcpu-1gb"
+ status = (known after apply)
+ urn = (known after apply)
+ vcpus = (known after apply)
+ volume_ids = (known after apply)
+ vpc_uuid = (known after apply)
}

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.

Enter a value: yes

Go to your dashboard in DigitalOcean and you can see your droplet being build.

If all went well you now have a DigitalOcean droplet with a new ip4 address

The file structure of your new Terraform repository should look like this minus the .gitigonore and readme.md since I opted for those when creating the respository.

filestructure so far of Terraform repository

Step 4 Destroying the test server

Since this was just a test we will delete the server. This is a fast and small proccess use the command terraform destroy to remove the server through the terminal.

But first use terraform plan to see if there are no changes.

terraform plan 

And now use terraform destroy to snap all your problems away.

terraform destroy
digitalocean_droplet.web: Refreshing state... [id=363523359]

Terraform used the selected providers to generate the following
execution plan. Resource actions are indicated with the following
symbols:
- destroy

Terraform will perform the following actions:

# digitalocean_droplet.web will be destroyed
- resource "digitalocean_droplet" "web" {
- backups = false -> null
- created_at = "2023-07-04T10:12:39Z" -> null
- disk = 25 -> null
- graceful_shutdown = false -> null
- id = "363523359" -> null
- image = "ubuntu-20-04-x64" -> null
- ipv4_address = "146.190.224.83" -> null
- ipv4_address_private = "10.110.0.3" -> null
- ipv6 = false -> null
- locked = false -> null
- memory = 1024 -> null
- monitoring = false -> null
- name = "testing-1" -> null
- price_hourly = 0.00893 -> null
- price_monthly = 6 -> null
- private_networking = true -> null
- region = "ams3" -> null
- resize_disk = true -> null
- size = "s-1vcpu-1gb" -> null
- status = "active" -> null
- tags = [] -> null
- urn = "do:droplet:363523359" -> null
- vcpus = 1 -> null
- volume_ids = [] -> null
- vpc_uuid = "b2df1bfc-9a94-4e61-9515-31aea95ea2dd" -> null
}

Plan: 0 to add, 0 to change, 1 to destroy.

Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.

Enter a value: yes

Since you have the Terraform file we can make a new droplet in seconds ! So don’t worry we will make a proper one in the next article.

Congratulations! You have successfully used Terraform to spin up a server with a ubuntu 20.4.x image. And of-course destroyed it because breaking things might appease Death.

Thanos snapping the servers away

Please reach out if you have any tips or tricks to improve this process so we can all learn from it.

Thank you for reading! If you found this article helpful, consider supporting me on my Patreon page at patreon.com/OjisanTech. Your support will contribute to covering server costs and keeping me fueled with snacks.

--

--

Alejo Cain

Software engineer that likes to talk about tools to build robots