Jan 12, 2024
Posted by
Mathis Van Eetvelde
Terraform is a must-have tool in any software developer’s toolbox. It allows for declarative configuration of cloud (and sometimes on-prem) resources. If used correctly, your cloud infrastructure is a true one-to-one representation of your configuration files and vice versa.
One of the biggest benefits of Infrastructure as Code (IaC) is increasing visibility into what your deployments really look like, which in turn helps with security, auditability, and accountability. My personal favorite benefit is how it increases the speed of deployment. Say goodbye to click-ops, because the speed at which Terraform allows you to deploy, reuse, and recycle configurations is supersonic.
Before joining Timescale as a developer advocate, I was a DevOps engineer at a British tech startup in the renewable energy sector. A significant part of my time here was spent deploying our infrastructure and software stack across fresh cloud accounts.
The infrastructure was entirely made up of Terraform modules that allowed me to mix and match required AWS services akin to Build-A-Bear. At most, my manual labor was limited to configuring the subnet CIDRs, modifying some DNS entries, and creating some SSH keys.
Everything else was done automatically, from deploying EC2 instances and Kubernetes clusters to databases and their tables. Thanks to Terraform’s idempotent nature, if something went wrong, all I had to do was re-run the Terraform deploy
command, and things would (usually) fix themselves.
We were able to move at a breakneck speed by leveraging Terraform, far surpassing the tedious process of manually deploying and configuring resources in the AWS Console. Needless to say, Terraform is an invaluable asset, having saved me countless weeks, if not months, of boring and repetitive work.
So, when Timescale announced internally they were working on their own Terraform provider, I was on cloud nine. Now, several months later, the Timescale Terraform provider has officially been released to General Availability, and I couldn’t be happier with it!
To celebrate this launch, I thought it would only be appropriate to write a blog post about what a Terraform provider is and how you can use the new Timescale provider to deploy and destroy Timescale services.
To use the Timescale Terraform provider, you must declare it in a required_providers
block nested inside a terraform
block. Set the source to timescale/timescale
as that corresponds to the official provider in the Terraform Registry.
While not strictly required, you can configure Terraform to use a specific version of the Timescale provider. To use the latest version, use the ~>
operator or omit the version argument altogether.
terraform {
required_providers {
timescale = {
source = "timescale/timescale"
version = "1.1.0"
}
}
}
After declaring the Timescale provider, you need to configure it with the ProjectID
and credentials.
provider "timescale" {
project_id = var.ts_project_id
access_key = var.ts_access_key
secret_key = var.ts_secret_key
}
While safely managing credentials in Terraform is a topic for another blog post, I like to keep it simple and create three variables for each parameter in a separate file. Though I highly recommend using a safer non-plain text solution like Hashicorp Vault, Terraform environment variables, or AWS KMS.
variable "ts_project_id" {
default = "your-project-id"
}
variable "ts_access_key" {
default = "your-access-key"
}
variable "ts_secret_key" {
default = "your-secret-key"
}
To retrieve the ProjectID
, go to the Services tab in Timescale and click on the kebab menu in the top right corner.
To create credentials, go to the Project Settings
tab and click Create credentials
. A window will pop up with your public and private keys. Make sure you copy both the public and private keys to a secure location (like a password manager), as this is the only time you will be able to retrieve them.
It’s also paramount to never share the private key with anyone you don’t 100 percent trust, as it would allow them to create and delete Timescale services at will until the credentials have been deleted.
You’ve successfully configured the Timescale provider; you can now create your first Timescale service using Terraform!
If you read the documentation, you’ll find that configuring a Timescale service through Terraform is very similar to creating a service through the advanced configuration menu in the Timescale UI.
First and foremost, you can give your Timescale service a name. Do note that the Terraform resource name and `name` parameters don’t need to be the same.
Next up is the CPU and memory configuration. Just like in the Timescale UI, only a handful of CPU and memory combinations are supported. Please consult the provider documentation for more information.
In this case, we have configured our Timescale instance with 1 CPU and 4 GB of memory. Timescale automatically allocates and charges only for the storage you use, therefore you don’t pre-provision a specific storage size. Please consult the provider documentation for more information.
For the region, I have chosen us-east-1
, but you can choose one of our supported regions.
Lastly, I added a prevent_destroy
lifecycle. This is a feature native to Terraform that prevents you from accidentally destroying your service. If a Timescale service is destroyed, the data it stores gets destroyed too, and because data loss is something we want to avoid at all costs, I think this is an absolute must.
If you do eventually want to delete your Timescale service and the corresponding data, you can remove or comment out the lifecycle.
resource "timescale_service" "my-resource" {
name = "my-resource"
milli_cpu = 1000
memory_gb = 4
region_code = "us-east-1"
lifecycle {
prevent_destroy = true
}
}
And that’s it! We are now ready to deploy our service! Execute the following commands in order. Enter yes
when Terraform prompts you to confirm the actions.
terraform init
terraform plan
terraform apply
Terraform will perform the following actions:
# timescale_service.my-resource will be created
+ resource "timescale_service" "my-resource" {
+ enable_ha_replica = false
+ hostname = (known after apply)
+ id = (known after apply)
+ memory_gb = 4
+ milli_cpu = 1000
+ name = "my-resource"
+ password = (sensitive value)
+ port = (known after apply)
+ region_code = "us-east-1"
+ username = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
timescale_service.my-resource: Creating...
timescale_service.my-resource: Creation complete after 38s [id=wcdokcbcd4]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Great! You’ve successfully deployed a Timescale service using Terraform. But how do you access your new service? Your first intuition might be to go to the Timescale UI and copy the connection string, but as you’ll soon notice, that string doesn’t include the password. The only way you can get the password is by using outputs!
output "service_hostname" {
value = timescale_service.my-resource.hostname
}
output "service_port" {
value = timescale_service.my-resource.port
}
output "service_username" {
value = timescale_service.my-resource.username
}
output "service_password" {
value = timescale_service.my-resource.password
sensitive = true
}
After adding the outputs, re-apply your configuration using the apply command and use the output command to retrieve the outputs you just created.
terraform apply
terraform output
As you can see, Terraform will gladly show you the hostname, port, and username, but it keeps the password hidden. This is because your service's password is a sensitive value, and it would be insecure to output it to the terminal on every terraform apply
because you never know where your logs will end up! To retrieve the password, we can use -json
flag to make Terraform print the outputs in JSON format.
terraform output -json
{
...
"service_password": {
"sensitive": true,
"type": "string",
"value": "vswm7wg2ls3cqpvd"
}
...
}
The wall of text it prints out into the terminal is less than ideal, so with another tool called jq we can filter out just the password.
terraform output -json | jq -r ".service_password.value"
If you know Terraform well enough, you can even synthesize the entire connection string as an output. This could be super useful when deploying other resources using Terraform that connect to your Timescale service.
output "service_url" {
value = format("postgres://tsdbadmin:%s@%s:%s/tsdb?sslmode=require",
timescale_service.my-resource.password,
timescale_service.my-resource.hostname,
timescale_service.my-resource.port)
sensitive = true
}
Having the connection string pre-synthesized also allows you to connect to your Timescale service using psql in a single command without even needing to print the password to the terminal!
psql $(terraform output -json | jq -r ".service_url.value")
When you’re all done with your Timescale service, you can shut it down using the terraform destroy
command. But not before you remove or comment out the prevent_destroy
lifecycle rule. Keep in mind that destroying your Timescale service will permanently delete your data!
terraform destroy
When the destroy command completes, the services you created will be gone.
If, like myself, you’re using Timescale as your cloud database and are a fan of the convenience and speed an open-source IaC tool like Terraform can bring, I know you’ll be as excited as I am about the Timescale Terraform provider.
Most developers are looking for tools that will make their work simpler, faster, and less tedious. However, they struggle to find them, making the dream of speedy deployments and time spent building instead of configuring a near impossibility.
At Timescale, we want to make it possible for software engineers to focus on their applications, not their databases. With the recent addition of the Timescale Terraform provider, managing your services with ease is now a reality. Try it yourself: sign up for a free 30-day trial (no credit card required).