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.