Terraform
Deploy an application to a DigitalOcean droplet
DigitalOcean is a cloud hosting provider for infrastructure and applications. DigitalOcean's cloud platform helps developers develop, manage, and scale their applications by providing simple workflows for complex infrastructure. DigitalOcean provides a lightweight, inexpensive virtual machine called a Droplet that you can deploy applications and services to. The Terraform DigitalOcean provider allows you to deploy and manage your Droplets and other infrastructure as code.
In this tutorial, you will use Terraform to provision a Digital Ocean Droplet and deploy a pre-built Hashicorp-skinned Tetris application to it. You will configure the Droplet with a cloud-init
script and specify an SSH key that can access the Droplet. Then, you will use Terraform to scale the application.
Prerequisites
For this tutorial, you will need:
- A DigitalOcean account
- Terraform v1.0.1+
jq
installed
Note
This tutorial provisions resources that qualify under the DigitalOcean Basic tier. If your account does not qualify under the Basic tier, we are not responsible for any charges that you may incur.
Generate DigitalOcean access token
To manage DigitalOcean resources with Terraform, you must configure the Terraform DigitalOcean provider with a DigitalOcean access token. The provider can access the token set as an environment variable and use it to authenticate with the DigitalOcean API.
To create a token, navigate to your API tokens page in the DigitalOcean console. Select "Generate New Token."
Keep the default scopes for "Read" and "Write" privileges. Enter do-terraform
as your token name and generate your token.
Copy the access token to your clipboard. DigitalOcean will not display this token again.
In your terminal, create an environment variable with your new personal access token. Replace the example value below with the token pasted from your clipboard.
$ export DIGITALOCEAN_ACCESS_TOKEN=
Review the example Terraform configuration
Clone the example repository that contains the Terraform DigitalOcean configuration.
$ git clone https://github.com/hashicorp/learn-terraform-digitalocean-droplet.git
Change into the repository directory.
$ cd learn-terraform-digitalocean-droplet
Open the main.tf
file. This configuration file defines a digitalocean_droplet
resource called terramino
based on an Ubuntu image. Terraform deploys a Droplet configured with a single CPU and 1 gig of RAM as defined by the size
attribute. The region
attribute instructs Terraform to deploy this resource to the nyc1
region.
main.tf
terraform {
required_version = "~> 1.0.0"
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.0"
}
}
}
provider "digitalocean" {}
resource "digitalocean_droplet" "terramino" {
image = "ubuntu-18-04-x64"
name = "terramino"
region = "nyc1"
size = "s-1vcpu-1gb"
user_data = file("terramino_app.yaml")
}
This is a complete configuration that you can deploy with Terraform. The following sections review each block of this configuration in more detail.
Terraform block
The terraform
block contains Terraform settings, including the providers required by your configuration.
main.tf
terraform {
required_version = "~> 1.0.0"
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.0"
}
}
}
In this example configuration, the required_providers
block installs the provider from the Terraform Registry. The DigitalOcean provider's source is defined as digitalocean/digitalocean
, which is shorthand for registry.terraform.io/digitalocean/digitalocean.
You can also set a version constraint for each provider defined in the required_providers
block. If you do not specify a provider version, Terraform will automatically download the latest version during initialization.
Providers
Providers are plugins that Terraform uses to create and manage your resources. To use a provider, you must define a provider block in your configuration. The provider
block in main.tf
configures the DigitalOcean provider you specified in your terraform
block. You can set your credentials in the provider
block, but it is safer to use the DIGITALOCEAN_ACCESS_TOKEN
environment variable to avoid committing sensitive values into source control. You can also configure other optional, provider-specific settings in this block.
main.tf
provider "digitalocean" {}
You can use multiple provider blocks in your Terraform configuration to manage resources from different providers. You can even reference data about resources from different providers. For example, you could pass the IP address of your DigitalOcean Droplet to a monitoring resource from DataDog.
Resources
A resource
block defines a component of your infrastructure. A resource might be a physical or virtual component such as a Droplet, or managed services such as the DigitalOcean App platform.
main.tf
resource "digitalocean_droplet" "terramino" {
image = "ubuntu-18-04-x64"
name = "terramino"
region = "nyc1"
size = "s-1vcpu-1gb"
user_data = file("terramino_app.yaml")
}
Resource blocks have two strings in the first line of block: the resource type and the resource name. In this example, the resource type is digitalocean_droplet
and the name is terramino
. The prefix of the type maps to the name of the provider. Terraform manages the digitalocean_droplet
resource using the digitalocean
provider. Together, the resource type and resource name form a unique ID for the resource. For example, the ID for your Droplet instance is digitalocean_droplet.terramino
.
Resource blocks contain arguments which you use to configure the resource. Arguments can include things like machine sizes, disk image names, or regions. The provider documentation defines the required and optional arguments for each resource. For your Droplet, the example configuration sets the image ID to an Ubuntu image and the size to s-1vcpu-1gb
, which qualifies for DigitealOcean's Basic tier. It also sets the instance name as terramino
.
This configuration uses the user_data
attribute and the file
function to pass a cloud-init script to your Droplet. Your Droplet will automatically run this cloud-init script and provision itself with your SSH key and the pre-built application.
Outputs
Open the outputs.tf
file.
outputs.tf
output "ip_address" {
value = digitalocean_droplet.terramino.ipv4_address
description = "The public IP address of your Droplet application."
}
Terraform displays output values when you apply your configuration. This configuration defines an output value for the IP address of your DigitalOcean Droplet.
You can use Terraform outputs to connect your Terraform projects with other parts of your infrastructure, or with other Terraform projects.
Create SSH key
To SSH into the Droplet and perform operations as the terraform
user, you must create an SSH key. Change the placeholder email address to your email address.
$ ssh-keygen -t rsa -C "your_email@example.com" -f ./tf-digitalocean
When prompted, press enter to create the SSH key without a passphrase.
Open the terramino_app.yaml
file and paste the contents of tf-digitalocean.pub
into the user data ssh_authorized_keys
section. Save the file.
terramino_app.yaml
##...
users:
- default
- name: terraform
gecos: terraform
primary_group: hashicorp
sudo: ALL=(ALL) NOPASSWD:ALL
groups: users, admin
ssh_import_id:
lock_passwd: false
ssh_authorized_keys:
+ - ssh-rsa AAAA….
- - # Paste your created SSH key here
##...
Deploy your application
Initialize your Terraform directory.
$ terraform init
Initializing the backend...
Initializing provider plugins...
- Reusing previous version of digitalocean/digitalocean from the dependency lock file
- Using previously-installed digitalocean/digitalocean v2.10.1
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.
Apply your configuration to deploy your application. Enter yes
to confirm your changes when prompted.
$ 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.terramino will be created
+ resource "digitalocean_droplet" "terramino" {
##...
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ ip_address = (known after apply)
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
digitalocean_droplet.terramino: Creating...
digitalocean_droplet.terramino: Creation complete after 1m31s [id=254026549]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
ip_address = "64.xxx.x.xxx"
In your web browser, navigate to the IP address in the Terraform output to verify your application deployed successfully. It may take a few moments for your Droplet to start Terramino. Refresh the page after a few minutes if nothing appears.
Modify your deployment
One of the benefits of using infrastructure as code is the ability to modify your resources after you deploy them.
Add a count
block to your digitalocean_droplet
block to scale your deployment.
main.tf
resource "digitalocean_droplet" "terramino" {
count = 2
image = "ubuntu-18-04-x64"
name = "terramino"
region = "nyc1"
size = "s-1vcpu-1gb"
user_data = file("terramino_app.yaml")
}
With the count
attribute in your configuration, you need to update your outputs to capture both IP addresses. Update value
of the output in the outputs.tf
file.
outputs.tf
output "ip_address" {
+ value = digitalocean_droplet.terramino[*].ipv4_address
- value = digitalocean_droplet.terramino.ipv4_address
description = "The public IP address of your droplet application."
}
The [*]
syntax is a splat expression that iterates over all of the elements of the list given to its left and returns a list of the given attribute from each.
Apply your configuration changes. Enter yes
when prompted to confirm your changes.
$ terraform apply
digitalocean_droplet.terramino[0]: Refreshing state... [id=256506932]
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.terramino[1] will be created
+ resource "digitalocean_droplet" "terramino" {
##...
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
~ ip_address = [
"159.203.185.122",
+ (known after apply),
]
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
digitalocean_droplet.terramino[1]: Creating...
digitalocean_droplet.terramino[1]: Still creating... [10s elapsed]
digitalocean_droplet.terramino[1]: Still creating... [20s elapsed]
digitalocean_droplet.terramino[1]: Creation complete after 28s [id=256507730]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
ip_address = [
"159.203.185.122",
"143.198.120.47",
]
You can use the terraform output
command to get the IP addresses of your Droplets for SSH. Use the output
command with the -json
flag to capture your outputs. Then, pipe that list to jq
and select the first element. Enter yes
when prompted to continue connecting to the instance. Make sure you are in the directory in which you generated your SSH key earlier.
$ ssh terraform@$(terraform output -json ip_address | jq -r '.[0]') -i tf-digitalocean
The authenticity of host '159.203.185.122 (159.203.185.122)' can't be established.
ECDSA key fingerprint is SHA256://f8d+jcZutHs25PBqGzVkogOVLkadQv/70p8c+eZ0Y.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '159.203.185.122' (ECDSA) to the list of known hosts.
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-144-generic x86_64)
##...
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
terraform@terramino:~$
Inspect your Apache access logs to review the connections to your application. The first address is the IP address you used to navigate to the application endpoint.
$ sudo tail -5 /var/log/apache2/access.log
73.xx.xx.xx - - [27/Jul/2021:19:39:30 +0000] "GET / HTTP/1.1" 200 3477 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"
73.xx.xx.xx - - [27/Jul/2021:19:39:30 +0000] "GET /icons/ubuntu-logo.png HTTP/1.1" 200 3623 "http://147.182.208.224/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"
73.xx.xx.xx - - [27/Jul/2021:19:39:31 +0000] "GET /favicon.ico HTTP/1.1" 404 493 "http://147.182.208.224/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"
73.xx.xx.xx - - [27/Jul/2021:19:39:38 +0000] "GET / HTTP/1.1" 200 9324 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"
Type exit
in your terminal to close the SSH connection.
$ exit
logout
Connection to 159.203.185.122 closed.
Clean up your infrastructure
To avoid unnecessary charges, destroy your application. In your terminal, destroy the resources provisioned by Terraform. Enter yes
when prompted to confirm your changes.
$ terraform destroy
digitalocean_droplet.terramino: Refreshing state... [id=254041301]
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.terramino will be destroyed
- resource "digitalocean_droplet" "terramino" {
##...
Plan: 0 to add, 0 to change, 1 to destroy.
Changes to Outputs:
- ip_address = "64.xxx.x.xxx" -> null
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
digitalocean_droplet.terramino: Destroying... [id=254041301]
digitalocean_droplet.terramino: Still destroying... [id=254041301, 11s elapsed]
digitalocean_droplet.terramino: Still destroying... [id=254041301, 21s elapsed]
digitalocean_droplet.terramino: Destruction complete after 27s
Destroy complete! Resources: 1 destroyed.
Next steps
In this tutorial, you used Terraform to provision a DigitalOcean Droplet with cloud-init
. Your user data script provisioned your Droplet with a public-facing application and secure credentials.
For more information about the DigitalOcean provider and the concepts used in this tutorial, visit the following documentation:
- DigitalOcean provider documentation
- DigitalOcean documentation
- Learn more about Terraform Outputs
- Learn more about Resources