🎉 Announcing new lower pricing — up to 40% lower costs for Cloud Servers and Cloud SQL! Read more →

Contents

Terraform on Brightbox

Terraform is a tool to make managing cloud infrastructure easy. You define the different components of your systems from as many different providers as you need and represent them all in code. Terraform works out all the dependencies, builds the components and configures all the relationships.

It’s focused very much on infrastructure, so once everything is built the baton is passed onto your configuration management tool of choice, such as Puppet or Ansible.

The result is a detailed description of everything you need to build your entire system from scratch.

Brightbox maintains a Terraform provider plugin to add support for managing Brightbox resources via our API.

This guide will take you through installing Terraform on your local machine and using it to build a small web cluster from a basic example configuration.

Install Terraform

There are detailed installation instructions in the official Terraform getting started guide, but the general idea is to download the appropriate package and unzip the binaries to somewhere in your PATH.

If you’re using Linux or similar, you could unzip them into your system-wide /usr/local/bin directory. Or you can put them somewhere in your home directory and add that to your PATH variable. E.g:

$ wget -q https://releases.hashicorp.com/terraform/0.12.28/terraform_0.12.28_linux_amd64.zip
$ sudo unzip terraform_0.12.28_linux_amd64.zip -d /usr/local/bin

Archive:  terraform_0.12.28_linux_amd64.zip
  inflating: /usr/local/bin/terraform

Clone our example Terraform configuration

Our example Terraform configuration defines a cloud server and a cloud sql instance, plus cloud ips and appropriate firewall rules for them.

Get the example configuration from our GitHub repository:

$ git clone git@github.com:brightbox/terraform-brightbox-example.git
$ cd terraform-brightbox-example

Setup authentication

Terraform authenticates with Brightbox using your username and password (or with API Client credentials if you prefer), plus you need to specify which of your accounts you want it work with (if you’ve not collaborated on any other accounts, you’ll have only one but you still need to specify the id).

You can configure your credentials statically in a tfvars file or with an environment variable. We recommend storing your account and username in a tfvars file, and to use an environment variable for your password, so it’s never written to disk.

So, create a file named local.auto.tfvars which terraform will read automatically:

account="acc-xxxxx"
username="john@example.com"

Then define your password using a the TF_VAR_password environment variable:

$ read -p "password: " -s TF_VAR_password

Or instead of a password, you can use a temporary access token. If you have our command line client setup, you can set that up like this:

$ export TF_VAR_password=$(brightbox token create --format=token)

Activate the brightbox plugin

Before using a configuration initialise terraform so that it knows about the Brightbox provider plugin

$ terraform init

Initializing the backend...

Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "brightbox" (terraform-providers/brightbox) 1.3.0...

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.

Execution plan

Now you have the example configuration checked out and your authentication configured, you can get terraform to show you its execution plan:

$ terraform plan

Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

data.brightbox_image.webserver: Refreshing state...

------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # brightbox_cloudip.db will be created
  + resource "brightbox_cloudip" "db" {
      + fqdn        = (known after apply)
      + id          = (known after apply)
      + locked      = (known after apply)
      + name        = "db ip"
      + public_ip   = (known after apply)
      + reverse_dns = (known after apply)
      + status      = (known after apply)
      + target      = (known after apply)
    }

  # brightbox_cloudip.webip will be created
  + resource "brightbox_cloudip" "webip" {
      + fqdn        = (known after apply)
      + id          = (known after apply)
      + locked      = (known after apply)
      + name        = "web ip"
      + public_ip   = (known after apply)
      + reverse_dns = (known after apply)
      + status      = (known after apply)
      + target      = (known after apply)
    }

  # brightbox_database_server.db will be created
  + resource "brightbox_database_server" "db" {
      + admin_password             = (sensitive value)
      + admin_username             = (known after apply)
      + allow_access               = (known after apply)
      + database_engine            = "mysql"
      + database_type              = (known after apply)
      + database_version           = "5.6"
      + id                         = (known after apply)
      + locked                     = false
      + maintenance_hour           = 2
      + maintenance_weekday        = 6
      + name                       = "db server"
      + snapshots_schedule         = (known after apply)
      + snapshots_schedule_next_at = (known after apply)
      + status                     = (known after apply)
      + zone                       = (known after apply)
    }

  # brightbox_firewall_policy.webservers will be created
  + resource "brightbox_firewall_policy" "webservers" {
      + id           = (known after apply)
      + name         = "web servers"
      + server_group = (known after apply)
    }

  # brightbox_firewall_rule.webservers_outbound will be created
  + resource "brightbox_firewall_rule" "webservers_outbound" {
      + description     = "Outbound internet access"
      + destination     = "any"
      + firewall_policy = (known after apply)
      + id              = (known after apply)
    }

  # brightbox_firewall_rule.webservers_ssh will be created
  + resource "brightbox_firewall_rule" "webservers_ssh" {
      + description      = "SSH access from anywhere"
      + destination_port = "22"
      + firewall_policy  = (known after apply)
      + id               = (known after apply)
      + protocol         = "tcp"
      + source           = "any"
    }

  # brightbox_firewall_rule.webservers_web will be created
  + resource "brightbox_firewall_rule" "webservers_web" {
      + description      = "HTTP/S access from anywhere"
      + destination_port = "80,443"
      + firewall_policy  = (known after apply)
      + id               = (known after apply)
      + protocol         = "tcp"
      + source           = "any"
    }

  # brightbox_server.webserver will be created
  + resource "brightbox_server" "webserver" {
      + fqdn                 = (known after apply)
      + hostname             = (known after apply)
      + id                   = (known after apply)
      + image                = "img-t9f9m"
      + interface            = (known after apply)
      + ipv4_address         = (known after apply)
      + ipv4_address_private = (known after apply)
      + ipv6_address         = (known after apply)
      + ipv6_hostname        = (known after apply)
      + locked               = false
      + name                 = "web server"
      + public_hostname      = (known after apply)
      + server_groups        = (known after apply)
      + status               = (known after apply)
      + type                 = "2gb.ssd"
      + username             = (known after apply)
      + zone                 = (known after apply)
    }

  # brightbox_server_group.webservers will be created
  + resource "brightbox_server_group" "webservers" {
      + id   = (known after apply)
      + name = "web servers"
    }

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

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

Apply

The plan looks good so let’s actually create the cluster. Just run terraform apply:

$ terraform apply

...

Plan: 9 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:

Type yes and hit enter. Terraform will then build your cluster:

brightbox_server_group.webservers: Creating...
brightbox_server_group.webservers: Creation complete after 0s [id=grp-1wf7h]
brightbox_database_server.db: Creating...
brightbox_firewall_policy.webservers: Creating...
brightbox_firewall_policy.webservers: Creation complete after 0s [id=fwp-mhtr7]
brightbox_firewall_rule.webservers_outbound: Creating...
brightbox_firewall_rule.webservers_web: Creating...
brightbox_server.webserver: Creating...
brightbox_firewall_rule.webservers_ssh: Creating...
brightbox_firewall_rule.webservers_web: Creation complete after 0s [id=fwr-k4f6a]
brightbox_firewall_rule.webservers_ssh: Creation complete after 0s [id=fwr-ghc1l]
brightbox_firewall_rule.webservers_outbound: Creation complete after 0s [id=fwr-mp4zw]
brightbox_database_server.db: Still creating... [10s elapsed]
brightbox_server.webserver: Still creating... [10s elapsed]
brightbox_database_server.db: Still creating... [20s elapsed]
brightbox_server.webserver: Still creating... [20s elapsed]
brightbox_server.webserver: Creation complete after 21s [id=srv-lhoa1]
brightbox_cloudip.webip: Creating...
brightbox_cloudip.webip: Creation complete after 1s [id=cip-9fx19]
brightbox_database_server.db: Still creating... [30s elapsed]
brightbox_database_server.db: Still creating... [40s elapsed]
brightbox_database_server.db: Still creating... [50s elapsed]
brightbox_database_server.db: Still creating... [1m0s elapsed]
brightbox_database_server.db: Still creating... [1m10s elapsed]
brightbox_database_server.db: Still creating... [1m20s elapsed]
brightbox_database_server.db: Still creating... [1m30s elapsed]
brightbox_database_server.db: Still creating... [1m40s elapsed]
brightbox_database_server.db: Still creating... [1m50s elapsed]
brightbox_database_server.db: Still creating... [2m0s elapsed]
brightbox_database_server.db: Creation complete after 2m3s [id=dbs-npcq7]
brightbox_cloudip.db: Creating...
brightbox_cloudip.db: Creation complete after 1s [id=cip-lkpmn]

Apply complete! Resources: 9 added, 0 changed, 0 destroyed.

Outputs:

db-admin-account-password = eupoorjdg336x4mp
db-ip-address = cip-lkpmn.gb1.brightbox.com
web-public-ip-address = cip-9fx19.gb1.brightbox.com

Test it

To demonstrate that everything has been set up and linked together correctly, test the MySQL connection from the new cloud server to the new cloud sql instance. Use the DNS names and password details from the Outputs section above:

$ ssh -l ubuntu cip-9fx19.gb1.brightbox.com

ubuntu@srv-lhoa1:~$ sudo apt-get update -qq
ubuntu@srv-lhoa1:~$ sudo apt-get install -qq -y mysql-client

ubuntu@srv-lhoa1:~$ mysql -h cip-lkpmn.gb1.brightbox.com -u admin -p
Enter password: 

Welcome to the MySQL monitor.  Commands end with ; or \g.

Your MySQL connection id is 36
Server version: 8.0.19-10 Percona Server (GPL), Release '10', Revision 'f446c04'

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show status like 'Uptime';
+---------------|-------+
| Variable_name | Value |
+---------------|-------+
| Uptime        | 378   |
+---------------|-------+
1 row in set (0.00 sec)

mysql>

Terraform state data

Terraform keeps track of what resources it creates and their attributes in a state file named terraform.tfstate in the current directory. This is required for terraform to continue to manage your cluster, so keep it safe.

For each new cluster you want to manage, just create a new directory with a copy of your configs in.

Provisioning

If this was a real cluster, you’d obviously need to configure your new cloud server and deploy your app to it. These steps are usually best left to configuration management tools like Puppet, Ansible or Chef but Terraform can be told how to execute these once the server is online. See their Terraform documentation on provisioners for more details.

Destroy it

Since this is just a test cluster, you can tear it all down now. Just run terraform destroy and it’ll destroy just those resources that it created as part of this example.

$ terraform destroy

...

Plan: 0 to add, 0 to change, 9 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

brightbox_firewall_rule.webservers_ssh: Destroying... [id=fwr-ghc1l]
brightbox_firewall_rule.webservers_web: Destroying... [id=fwr-k4f6a]
brightbox_cloudip.db: Destroying... [id=cip-lkpmn]
brightbox_cloudip.webip: Destroying... [id=cip-9fx19]
brightbox_firewall_rule.webservers_outbound: Destroying... [id=fwr-mp4zw]
brightbox_firewall_rule.webservers_outbound: Destruction complete after 0s
brightbox_firewall_rule.webservers_ssh: Destruction complete after 0s
brightbox_firewall_rule.webservers_web: Destruction complete after 0s
brightbox_cloudip.webip: Destruction complete after 1s
brightbox_server.webserver: Destroying... [id=srv-lhoa1]
brightbox_cloudip.db: Destruction complete after 2s
brightbox_database_server.db: Destroying... [id=dbs-npcq7]
brightbox_server.webserver: Still destroying... [id=srv-lhoa1, 10s elapsed]
brightbox_database_server.db: Still destroying... [id=dbs-npcq7, 10s elapsed]
brightbox_server.webserver: Destruction complete after 11s
brightbox_firewall_policy.webservers: Destroying... [id=fwp-mhtr7]
brightbox_firewall_policy.webservers: Destruction complete after 0s
brightbox_database_server.db: Destruction complete after 11s
brightbox_server_group.webservers: Destroying... [id=grp-1wf7h]
brightbox_server_group.webservers: Destruction complete after 0s

Destroy complete! Resources: 9 destroyed.

If you’d rather not completely trust Terraform, remember you can use our “resource lock” feature to prevent accidental deletions of important resources (and you can even have terraform lock important servers).

Further details

You can read more about the resources and data sources available in the Brightbox Terraform Provider in the documentation section of the Terraform website

And for a realworld detailed example set of terraform configs for managing a complex cluster, see our Kubernetes terraform manifests.

Last updated: 16 Aug 2022 at 09:23 UTC

Try Brightbox risk-free with £50 free credit Sign up takes just two minutes...