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. 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. 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=
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.
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.
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.
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.
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.
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. 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 "[email protected]" -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.
##... 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 ##...
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. 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.
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.
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.
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.
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
Stop the war in Ukraine. Terraform & DigitalOcean
Preface.
Infrastructure as Code (IaC) is the management of infrastructure (networks, virtual machines, load balancers, and connection topology) in a descriptive model, using the same versioning as the DevOps team uses for source code. Like the principle that the same source code generates the same binary, and the IaC model generates the same environment every time it is applied. Terraform is an open-source infrastructure as code software tool created by HashiCorp. Users define and provide data center infrastructure using a declarative configuration language known as HashiCorp Configuration Language (HCL). In this article, we will configure the Droplet with a cloud-init script and specify an SSH key that can access the Droplet.
Step 1. Create personal access token.
Personal access tokens function like ordinary OAuth access tokens. You can use them to authenticate to the API by including one in a bearer-type Authorization
header with your request.
To generate a personal access token, log in to the DigitalOcean Control Panel. Click the API link in the main navigation, which takes you to the Applications & API page on the Tokens/Keys tab. In the Personal access tokens section, click the Generate New Token button. This opens a new personal access token window: Generate Token When you click Generate Token, your token is generated and presented to you on your Personal Access Tokens page. The actual token is the long string of numbers and letters, under the name. It will be prefixed with dop_v1_
to distinguish it from other similar tokens.
Step 2. Environment variable
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=YOUR_DO_TOKEN_HERE
Example: DO token export
Step 3. Create terraform configuration.
terraform code example The terraform block contains Terraform settings, including the providers required by your configuration. Providers are plugins that Terraform uses to create and manage your resources. In locals, I listed a map of pre-named sizes to look up from and a map of regions. In my example
region = local.regions.toronto size = local.sizes.nano
So you can just choose necessary for you. A resource block defines a component of your infrastructure. It might be a physical or virtual component such as a Droplet.
We will create digitalocean_droplet & digitalocean_tag. User-data is a string of the desired user data for the droplet.
The most common type of script to pass in is called a cloud-config script. This is a YAML formatted file that provides simple, readable methods of setting up common configuration items by declaration. It also can run arbitrary commands for other tasks.
In this block of code we will:
1. Create the new user
2. Set up sudo access
3. Add user to necessary groups
4. Set the shell environment
5. Install OS updates, some base software, and docker.
user_data = <<EOF#cloud-config groups: - ubuntu: [root,sys]# Add users to the system. Users are added after groups are added. users: - default - name: lesha gecos: lesha shell: /bin/bash primary_group: lesha sudo: ALL=(ALL) NOPASSWD:ALL groups: users, admin, docker lock_passwd: false ssh_authorized_keys: - ssh-rsa PLACE YOUR PUBLIC KEY HEREruncmd: - sudo apt-get -y update - sudo apt -y install apt-transport-https ca-certificates curl software-properties-common net-tools - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - - sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable" - sudo apt -y update - sudo apt-cache policy docker-ce && apt-get -y install docker-ce - sudo usermod -aG docker leshaEOF
Terraform displays output values when you apply your configuration. This configuration defines an output value for the IP address of your DigitalOcean Droplet.
Step 4. Run terraform init
The terraform init command is used to initialize a working directory containing Terraform configuration files. This is the first command that should be run after writing a new Terraform configuration or cloning an existing one from version control. It is safe to run this command multiple time
$ terraform init
terraform init
Step 5. Run terraform plan command.
The terraform plan command creates an execution plan, which lets you preview the changes that terraform plans to make to your infrastructure. By default, when terraform creates a plan it:
- Reads the current state of any already-existing remote objects to make sure that the Terraform state is up-to-date.
- Compares the current configuration to the prior state and notes any differences.
- Proposes a set of change actions that should, if applied, make the remote objects match the configuration.
$ terraform plan
terraform plan
Step 6. Run terraform apply command.
The terraform apply command executes the actions proposed in a Terraform plan.
$ terraform apply
terraform apply Let’s check in the DIgitalOcean control panel: Droplet creatingNew Droplet Looks like our new droplet is up and running.
Step 7. Connect to the droplet via ssh.
ssh [email protected]
You should use username from the 3rd step and IP address from the 5th step (output section). ssh to new droplet
Step 8. Check if Docker is installed.
Finally, I will check is docker installed and can it be run without sudo. docker version output
Introduction
Terraform is a tool for building and managing infrastructure in an organized way. You can use it to manage DigitalOcean Droplets, Load Balancers, and even DNS entries, in addition to a large variety of services offered by other providers. Terraform uses a command-line interface and can run from your desktop or a remote server. Terraform works by reading configuration files that describe the components that make up your application environment or datacenter. Based on the configuration, it generates an execution plan that describes what it will do to reach the desired state. You then use Terraform to execute this plan to build the infrastructure. When changes to the configuration occur, Terraform can generate and execute incremental plans to update the existing infrastructure to the newly described state. In this tutorial, you’ll install Terraform and use it to create an infrastructure on DigitalOcean that consists of two Nginx servers that are load balanced by a DigitalOcean Load Balancer. Then, you’ll use Terraform to add a DNS entry on DigitalOcean that points to your Load Balancer. This will help you get started with using Terraform, and give you an idea of how you can use it to manage and deploy a DigitalOcean-based infrastructure that meets your own needs. Note: This tutorial has been tested with Terraform 1.1.3
.
Prerequisites
To complete this tutorial, you’ll need:
- A DigitalOcean account. If you do not have one, sign up for a new account.
- A DigitalOcean Personal Access Token, which you can create via the DigitalOcean control panel. Instructions to do that can be found at: How to Create a Personal Access Token.
- A password-less SSH key added to your DigitalOcean account, which you can create by following How To Use SSH Keys with DigitalOcean Droplets. When you add the key to your account, remember the name you give it, as you’ll need it in this tutorial. (For Terraform to accept the name of your key, it must start with a letter or underscore and may contain only letters, digits, underscores, and dashes.)
- A personal domain pointed to DigitalOcean’s nameserver, which you can do by following the tutorial, How To Point to DigitalOcean Nameservers From Common Domain Registrars.
Step 1 — Installing Terraform
Terraform is a command-line tool that you run on your desktop or on a remote server. To install it, you’ll download it and place it on your PATH
so you can execute it in any directory you’re working in. First, download the appropriate package for your OS and architecture from the official Downloads page. If you’re on macOS or Linux, you can download Terraform with curl
. On macOS, use this command to download Terraform and place it in your home directory:
- curl -o ~/terraform.zip https://releases.hashicorp.com/terraform/1.1.3/terraform_1.1.3_darwin_amd64.zip
On Linux, use this command:
- curl -o ~/terraform.zip https://releases.hashicorp.com/terraform/1.1.3/terraform_1.1.3_linux_amd64.zip
Create the ~/opt/terraform
directory:
- mkdir -p ~/opt/terraform
Then, unzip Terraform to ~/opt/terraform
using the unzip
command. On Ubuntu, you can install unzip
using apt
:
- sudo apt install unzip
Use it to extract the downloaded archive to the ~/opt/terraform
directory by running:
- unzip ~/terraform.zip -d ~/opt/terraform
Finally, add ~/opt/terraform
to your PATH
environment variable so you can execute the terraform
command without specifying the full path to the executable. On Linux, you’ll need to redefine PATH
in .bashrc
, which runs when a new shell opens. Open it for editing by running:
- nano ~/.bashrc
Note: On macOS, add the path to the file .bash_profile
if using Bash, or to .zshrc
if using ZSH. To append Terraform’s path to your PATH, add the following line at the end of the file: .bashrc
export PATH=$PATH:~/opt/terraform
Save and close the file when you’re done. Now all of your new shell sessions will be able to find the terraform
command. To load the new PATH
into your current session, run the following command if you’re using Bash on a Linux system:
- . ~/.bashrc
If you’re using Bash on macOS, execute this command instead:
- . .bash_profile
If you’re using ZSH, run this command:
- . .zshrc
To verify that you have installed Terraform correctly, run the terraform
command with no arguments:
- terraform
You will see output that is similar to the following:
Output
Usage: terraform [global options] <subcommand> [args] The available commands for execution are listed below. The primary workflow commands are given first, followed by less common or more advanced commands. Main commands: init Prepare your working directory for other commands validate Check whether the configuration is valid plan Show changes required by the current configuration apply Create or update infrastructure destroy Destroy previously-created infrastructure All other commands: console Try Terraform expressions at an interactive command prompt fmt Reformat your configuration in the standard style force-unlock Release a stuck lock on the current workspace get Install or upgrade remote Terraform modules graph Generate a Graphviz graph of the steps in an operation import Associate existing infrastructure with a Terraform resource login Obtain and save credentials for a remote host logout Remove locally-stored credentials for a remote host output Show output values from your root module providers Show the providers required for this configuration refresh Update the state to match remote systems show Show the current state or a saved plan state Advanced state management taint Mark a resource instance as not fully functional test Experimental support for module integration testing untaint Remove the ‘tainted’ state from a resource instance version Show the current Terraform version workspace Workspace management Global options (use these before the subcommand, if any): -chdir=DIR Switch to a different working directory before executing the given subcommand. -help Show this help output, or the help for a specified subcommand. -version An alias for the «version» subcommand.
These are the commands that Terraform accepts. The output gives you a brief description, and you’ll learn more about them throughout this tutorial. Now that Terraform is installed, let’s configure it to work with DigitalOcean’s resources.
Step 2 — Configuring Terraform for DigitalOcean
Terraform supports a variety of service providers through providers you can install. Each provider has its own specifications, which generally map to the API of its respective service provider. The DigitalOcean provider lets Terraform interact with the DigitalOcean API to build out infrastructure. This provider supports creating various DigitalOcean resources, including the following:
- digitalocean_droplet: Droplets (servers)
- digitalocean_loadbalancer: Load Balancers
- digitalocean_domain: DNS domain entries
- digitalocean_record: DNS records
Terraform will use your DigitalOcean Personal Access Token to communicate with the DigitalOcean API and manage resources in your account. Don’t share this key with others, and keep it out of scripts and version control. Export your DigitalOcean Personal Access Token to an environment variable called DO_PAT
by running:
- export DO_PAT=»your_personal_access_token«
This will make using it in subsequent commands easier and keep it separate from your code. Note: If you’ll be working with Terraform and DigitalOcean often, add this line to your shell configuration files using the same approach you used to modify your PATH
environment variable in the previous step. Create a directory that will store your infrastructure configuration by running the following command:
- mkdir ~/loadbalance
Navigate to the newly created directory:
- cd ~/loadbalance
Terraform configurations are text files that end with the .tf
file extension. They are human-readable and they support comments. (Terraform also supports JSON-format configuration files, but they won’t be covered here.) Terraform will read all of the configuration files in your working directory in a declarative manner, so the order of resource and variable definitions do not matter. Your entire infrastructure can exist in a single configuration file, but you should separate the configuration files by resource type to maintain clarity. The first step to building an infrastructure with Terraform is to define the provider you’re going to use. To use the DigitalOcean provider with Terraform, you have to tell Terraform about it and configure the plugin with the proper credential variables. Create a file called provider.tf
, which will store the configuration for the provider:
- nano provider.tf
Add the following lines into the file to tell Terraform that you want to use the DigitalOcean provider, and instruct Terraform where to find it: ~/loadbalance/provider.tf
terraform { required_providers { digitalocean = { source = "digitalocean/digitalocean" version = "~> 2.0" } } }
Then, define the following variables in the file so you can reference them in the rest of your configuration files:
do_token
: your DigitalOcean Personal Access Token.pvt_key
: private key location, so Terraform can use it to log in to new Droplets and install Nginx.
You will pass the values of these variables into Terraform when you run it, rather than by hard-coding the values here. This makes the configuration more portable. To define these variables, add these lines to the file: ~/loadbalance/provider.tf
... variable "do_token" {} variable "pvt_key" {}
Then, add these lines to configure the DigitalOcean provider and specify the credentials for your DigitalOcean account by assigning the do_token
to the token
argument of the provider: ~/loadbalance/provider.tf
... provider "digitalocean" { token = var.do_token }
Finally, you’ll want to have Terraform automatically add your SSH key to any new Droplets you create. When you added your SSH key to DigitalOcean, you gave it a name. Terraform can use this name to retrieve the public key. Add these lines, replacing terraform
with the name of the key you provided in your DigitalOcean account: ~/loadbalance/provider.tf
... data "digitalocean_ssh_key" "terraform" { name = "terraform" }
Your completed provider.tf
file will look like this: ~/loadbalance/provider.tf
terraform { required_providers { digitalocean = { source = "digitalocean/digitalocean" version = "~> 2.0" } } } variable "do_token" {} variable "pvt_key" {} provider "digitalocean" { token = var.do_token } data "digitalocean_ssh_key" "terraform" { name = "terraform" }
When you’re done, save and close the file. Note: Setting the TF_LOG
environment variable to 1
will enable detailed logging of what Terraform is trying to do. You can set it by running:
- export TF_LOG=1
Initialize Terraform for your project by running:
- terraform init
This will read your configuration and install the plugins for your provider. You’ll see that logged in the output:
Output
Initializing the backend… Initializing provider plugins… — Finding digitalocean/digitalocean versions matching «~> 2.0″… — Installing digitalocean/digitalocean v2.16.0… — Installed digitalocean/digitalocean v2.16.0 (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.
If you happen to get stuck, and Terraform is not working as you expect, you can start over by deleting the terraform.tfstate
file and manually destroying the resources that were created (e.g., through the control panel). Terraform is now configured and can be connected to your DigitalOcean account. In the next step, you’ll use Terraform to define a Droplet that will run an Nginx server.
Step 3 — Defining the First Nginx Server
You can use Terraform to create a DigitalOcean Droplet and install software on the Droplet once it spins up. In this step, you’ll provision a single Ubuntu 20.04 Droplet and install the Nginx web server using Terraform. Create a new Terraform configuration file called www-1.tf
, which will hold the configuration for the Droplet:
- nano www-1.tf
Insert the following lines to define the Droplet resource: ~/loadbalance/www-1.tf
resource "digitalocean_droplet" "www-1" { image = "ubuntu-20-04-x64" name = "www-1" region = "nyc3" size = "s-1vcpu-1gb" ssh_keys = [ data.digitalocean_ssh_key.terraform.id ]
In the preceding configuration, the first line defines a digitalocean_droplet resource named www-1
. The rest of the lines specify the Droplet’s attributes, including the data center it will be residing in and the slug that identifies the size of the Droplet you want to configure. In this case you’re using s-1vcpu-1gb
, which will create a Droplet with one CPU and 1GB of RAM. (Visit this size slug chart to see the available slugs you can use.) The ssh_keys
section specifies a list of public keys you want to add to the Droplet. In this case you’re specifying the key you defined in provider.tf
. Ensure the name here matches the name you specified in provider.tf
. When you run Terraform against the DigitalOcean API, it will collect a variety of information about the Droplet, such as its public and private IP addresses. This information can be used by other resources in your configuration. If you are wondering which arguments are required or optional for a Droplet resource, please refer to the official Terraform documentation: DigitalOcean Droplet Specification. To set up a connection
that Terraform can use to connect to the server via SSH, add the following lines at the end of the file: ~/loadbalance/www-1.tf
... connection { host = self.ipv4_address user = "root" type = "ssh" private_key = file(var.pvt_key) timeout = "2m" }
These lines describe how Terraform should connect to the server, so Terraform can connect over SSH to install Nginx. Note the use of the private key variable var.pvt_key
—you’ll pass its value in when you run Terraform. Now that you have the connection set up, configure the remote-exec
provisioner, which you’ll use to install Nginx. Add the following lines to the configuration to do just that: ~/loadbalance/www-1.tf
... provisioner "remote-exec" { inline = [ "export PATH=$PATH:/usr/bin", # install nginx "sudo apt update", "sudo apt install -y nginx" ] } }
Note that the strings in the inline
array are the commands that the root user will run to install Nginx. The completed file looks like this: ~/loadbalance/www-1.tf
resource "digitalocean_droplet" "www-1" { image = "ubuntu-20-04-x64" name = "www-1" region = "nyc3" size = "s-1vcpu-1gb" ssh_keys = [ data.digitalocean_ssh_key.terraform.id ] connection { host = self.ipv4_address user = "root" type = "ssh" private_key = file(var.pvt_key) timeout = "2m" } provisioner "remote-exec" { inline = [ "export PATH=$PATH:/usr/bin", # install nginx "sudo apt update", "sudo apt install -y nginx" ] } }
Save the file and exit the editor. You’ve defined the server, and are ready to deploy it, which you’ll now do.
Step 4 — Using Terraform to Create the Nginx Server
Your current Terraform configuration describes a single Nginx server. You’ll now deploy the Droplet exactly as it’s defined. Run the terraform plan
command to see the execution plan, or what Terraform will attempt to do to build the infrastructure you described. You will have to specify the values for your DigitalOcean Access Token and the path to your private key, as your configuration uses this information to access your Droplet to install Nginx. Run the following command to create a plan:
- terraform plan \
- -var «do_token=${DO_PAT}» \
- -var «pvt_key=$HOME/.ssh/id_rsa»
Warning: The terraform plan
command supports an -out
parameter to save the plan. However, the plan will store API keys, and Terraform does not encrypt this data. When using this option, you should explore encrypting this file if you plan to send it to others or leave it at rest for an extended period of time. You’ll see output similar to this:
Output
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.www-1 will be created + resource «digitalocean_droplet» «www-1» { + 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 = «www-1» + price_hourly = (known after apply) + price_monthly = (known after apply) + private_networking = (known after apply) + region = «nyc3» + resize_disk = true + size = «s-1vcpu-1gb» + ssh_keys = [ + «…», ] + 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. ─────────────────────────────────────────────────────────────── Note: You didn’t use the -out option to save this plan, so Terraform can’t guarantee to take exactly these actions if you run «terraform apply» now.
The + resource "digitalocean_droplet" "www-1"
line means that Terraform will create a new Droplet resource called www-1
, with the details that follow it. That’s exactly what should happen, so run terraform apply
command to execute the current plan:
- terraform apply \
- -var «do_token=${DO_PAT}» \
- -var «pvt_key=$HOME/.ssh/id_rsa»
You’ll get the same output as before, but this time, Terraform will ask you if you want to proceed:
Output
… 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
Enter yes
and press ENTER
. Terraform will provision your Droplet:
Output
digitalocean_droplet.www-1: Creating…
After a bit of time, you’ll see Terraform installing Nginx with the remote-exec
provisioner, and then the process will complete:
Output
digitalocean_droplet.www-1: Provisioning with ‘remote-exec’… …. digitalocean_droplet.www-1: Creation complete after 1m54s [id=your_www-1_droplet_id] Apply complete! Resources: 1 added, 0 changed, 0 destroyed. …
Terraform has created a new Droplet called www-1
and installed Nginx on it. If you visit the public IP address of your new Droplet, you’ll see the Nginx welcome screen. The public IP was displayed when the Droplet was created, but you can always view it by looking at Terraform’s current state. Terraform updates the state file terraform.tfstate
every time it executes a plan or refreshes its state. To view the current state of your environment, use the following command:
- terraform show terraform.tfstate
This will show you the public IP address of your Droplet.
Output
resource «digitalocean_droplet» «www-1» { backups = false created_at = «…» disk = 25 id = «your_www-1_droplet_id» image = «ubuntu-20-04-x64» ipv4_address = «your_www-1_server_ip» ipv4_address_private = «10.128.0.2» …
Navigate to http://your_www-1_server_ip
in your browser to verify your Nginx server is running. Note: If you modify your infrastructure outside of Terraform, your state file will be out of date. If your resources are modified outside of Terraform, you’ll need to refresh the state file to bring it up to date. This command will pull the updated resource information from your provider(s):
- terraform refresh \
- -var «do_token=${DO_PAT}» \
- -var «pvt_key=$HOME/.ssh/id_rsa»
In this step, you’ve deployed the Droplet that you’ve described in Terraform. You’ll now create a second one.
Step 5 — Creating the Second Nginx Server
Now that you have described an Nginx server, you can add a second quickly by copying the existing server’s configuration file and replacing the name and hostname of the Droplet resource. You can do this manually, but it’s faster to use the sed
command to read the www-1.tf
file, substitute all instances of www-1
with www-2
, and create a new file called www-2.tf
. Here is the sed
command to do that:
- sed ‘s/www-1/www-2/g’ www-1.tf > www-2.tf
You can learn more about sed
by visiting Using sed. Run terraform plan
again to preview the changes that Terraform will make:
- terraform plan \
- -var «do_token=${DO_PAT}» \
- -var «pvt_key=$HOME/.ssh/id_rsa»
The output shows that Terraform will create the second server, www-2
:
Output
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.www-2 will be created + resource «digitalocean_droplet» «www-2» { + backups = false + created_at = (known after apply) + disk = (known after apply) + 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 = «www-2» + price_hourly = (known after apply) + price_monthly = (known after apply) + private_networking = true + region = «nyc3» + resize_disk = true + size = «s-1vcpu-1gb» + ssh_keys = [ + «…», ] + 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. …
Run terraform apply
again to create the second Droplet:
- terraform apply \
- -var «do_token=${DO_PAT}» \
- -var «pvt_key=$HOME/.ssh/id_rsa»
As before, Terraform will ask you to confirm you wish to proceed. Review the plan again and type yes
to continue. After some time, Terraform will create the new server and display the results:
Output
digitalocean_droplet.www-2: Creation complete after 1m47s [id=your_www-2_droplet_id] … Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Terraform created the new server, while not altering the existing one. You can repeat this step to add additional Nginx servers. Now that you have two Droplets running Nginx, you’ll define and deploy a load balancer to split traffic between them.
Step 6 — Creating the Load Balancer
You’ll use a DigitalOcean Load Balancer, which the official Terraform provider supports, to route traffic between the two web servers. Create a new Terraform configuration file called loadbalancer.tf
:
- nano loadbalancer.tf
Add the following lines to define the Load Balancer: ~/loadbalance/loadbalancer.tf
resource "digitalocean_loadbalancer" "www-lb" { name = "www-lb" region = "nyc3" forwarding_rule { entry_port = 80 entry_protocol = "http" target_port = 80 target_protocol = "http" } healthcheck { port = 22 protocol = "tcp" } droplet_ids = [digitalocean_droplet.www-1.id, digitalocean_droplet.www-2.id ] }
The Load Balancer definition specifies its name, the datacenter it will be in, the ports it should listen on to balance traffic, configuration for the health check, and the IDs of the Droplets it should balance, which you fetch using Terraform variables. Save and close the file. Run terraform plan
command again to review the new execution plan:
- terraform plan \
- -var «do_token=${DO_PAT}» \
- -var «pvt_key=$HOME/.ssh/id_rsa»
You’ll see several lines of output, including the following lines:
Output
… 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_loadbalancer.www-lb will be created + resource «digitalocean_loadbalancer» «www-lb» { + algorithm = «round_robin» + disable_lets_encrypt_dns_records = false + droplet_ids = [ + …, + …, ] + enable_backend_keepalive = false + enable_proxy_protocol = false + id = (known after apply) + ip = (known after apply) + name = «www-lb» + redirect_http_to_https = false + region = «nyc3» + size_unit = (known after apply) + status = (known after apply) + urn = (known after apply) + vpc_uuid = (known after apply) + forwarding_rule { + certificate_id = (known after apply) + certificate_name = (known after apply) + entry_port = 80 + entry_protocol = «http» + target_port = 80 + target_protocol = «http» + tls_passthrough = false } + healthcheck { + check_interval_seconds = 10 + healthy_threshold = 5 + port = 22 + protocol = «tcp» + response_timeout_seconds = 5 + unhealthy_threshold = 3 } + sticky_sessions { + cookie_name = (known after apply) + cookie_ttl_seconds = (known after apply) + type = (known after apply) } } Plan: 1 to add, 0 to change, 0 to destroy. …
This means that the www-1
and www-2
Droplets already exist, and Terraform will create the www-lb
Load Balancer. Run terraform apply
to build the Load Balancer:
- terraform apply \
- -var «do_token=${DO_PAT}» \
- -var «pvt_key=$HOME/.ssh/id_rsa»
Once again, Terraform will ask you to review the plan. Approve the plan by entering yes
to continue. Once you do, you’ll see output that contains the following lines, truncated for brevity:
Output
… digitalocean_loadbalancer.www-lb: Creating… … digitalocean_loadbalancer.www-lb: Creation complete after 1m18s [id=your_load_balancer_id] … Apply complete! Resources: 1 added, 0 changed, 0 destroyed. …
Use terraform show terraform.tfstate
to locate the IP address of your Load Balancer:
- terraform show terraform.tfstate
You’ll find the IP under the www-lb
entry:
Output
… # digitalocean_loadbalancer.www-lb: resource «digitalocean_loadbalancer» «www-lb» { algorithm = «round_robin» disable_lets_encrypt_dns_records = false droplet_ids = [ your_www-1_droplet_id, your_www-2_droplet_id, ] enable_backend_keepalive = false enable_proxy_protocol = false id = «your_load_balancer_id» ip = «your_load_balancer_ip» name = «www-lb» …
Navigate to http://your_load_balancer_ip
in your browser and you’ll see an Nginx welcome screen because the Load Balancer is sending traffic to one of the two Nginx servers. You’ll now learn how to configure DNS for your DigitalOcean account using Terraform.
Step 7 — Creating DNS Domains and Records
In addition to Droplets and Load Balancers, Terraform can also create DNS domain and record domains. For example, if you want to point your domain to your Load Balancer, you can write the configuration describing that relationship. Note: Use your own, unique domain name or Terraform will be unable to deploy the DNS resources. Be sure your domain is pointed to DigitalOcean nameservers. Create a new file to describe your DNS:
- nano domain_root.tf
Add the following domain resource, replacing your_domain
with your domain name: ~/loadbalance/domain_root.tf
resource "digitalocean_domain" "default" { name = "your_domain" ip_address = digitalocean_loadbalancer.www-lb.ip }
Save and close the file when you’re done. You can also add a CNAME record that points www.your_domain
to your_domain
. Create a new file for the CNAME record:
- nano domain_cname.tf
Add these lines to the file: domain_cname.tf
resource "digitalocean_record" "CNAME-www" { domain = digitalocean_domain.default.name type = "CNAME" name = "www" value = "@" }
Save and close the file when you’re done. To add the DNS entries, run terraform plan
followed by terraform apply
, as with the other resources. Navigate to your domain name and you’ll see an Nginx welcome screen because the domain is pointing to the Load Balancer, which is sending traffic to one of the two Nginx servers.
Step 8 — Destroying Your Infrastructure
Although not commonly used in production environments, Terraform can also destroy infrastructure that it created. This is mainly useful in development environments that are deployed and destroyed multiple times. First, create an execution plan to destroy the infrastructure by using terraform plan -destroy
:
- terraform plan -destroy -out=terraform.tfplan \
- -var «do_token=${DO_PAT}» \
- -var «pvt_key=$HOME/.ssh/id_rsa»
Terraform will output a plan with resources marked in red, and prefixed with a minus sign, indicating that it will delete the resources in your infrastructure. Then, use terraform apply
to run the plan:
- terraform apply terraform.tfplan
Terraform will proceed to destroy the resources, as indicated in the generated plan.
Conclusion
In this tutorial, you used Terraform to build a load-balanced web infrastructure on DigitalOcean, with two Nginx web servers running behind a DigitalOcean Load Balancer. You know how to create and destroy resources, view the current state, and use Terraform to configure DNS entries. Now that you understand how Terraform works, you can create configuration files that describe a server infrastructure for your own projects. The example in this tutorial is a good starting point that demonstrates how you can automate the deployment of servers. If you already use provisioning tools, you can integrate them with Terraform to configure servers as part of their creation process instead of using the provisioning method used in this tutorial. Terraform has many more features, and can work with other providers. Check out the official Terraform Documentation to learn more about how you can use Terraform to improve your own infrastructure. This tutorial is part of the How To Manage Infrastructure with Terraform series. The series covers a number of Terraform topics, from installing Terraform for the first time to managing complex projects.
- How to convince your parents to let you homeschool
- How to respond when someone says they have cancer
- How to make digital art
- How to find rss feeds on the web
- How to make an anonymous call