Terraform
Introduction
Terraform is an infrastructure as code (IaC) tool that allows you to build, change, and version infrastructure safely and efficiently. This includes both low-level components like compute instances, storage, and networking, as well as high-level components like DNS entries and SaaS features.
Easily provision elements in the cloud
The core functionality of Bunnyshell is to allow users to easily create environments where they can test their applications. However, if you want to create any elements in the cloud, you will need to use Terraform. It is independent from our Kubernetes integration and it lets you define cloud resources in configuration files that you can version, share and reuse.
Preparing your Terraform Module directory
Module structure
Below you will find a typical file structure for a Terraform module:
.
├── LICENSE
├── README.md
├── main.tf
├── variables.tf
├── outputs.tf
None of these files are required, or have any special meaning to Terraform when it uses your module. According to Terraform documentation, you can create a module with a single .tf file, or use any other file structure you like.
Each of the files listed above serves a purpose:
- LICENSE contains the license under which the module is distributed. The LICENSE file shows the terms under which a shared Terraform module has been made available. Terraform does not use this file.
- README.md contains the documentation for your module, in markdown format. Although Terraform does not use it, services like Terraform Registry and GitHub will display the contents of this file.
- main.tf contains the main set of configuration for your module. You can also create other configuration files and organize them in a way which suits your needs.
- variables.tf contains the variable definitions for your module. When your module is used by others, the variables are configured as arguments in the module block. Any variables that are not given a default value will become required arguments. Variables with default values can also be provided as module arguments, overriding the default value.
- outputs.tf will contain the output definitions for your module. Module outputs are made available to the configuration using the module, so they are often used to pass information about the parts of your infrastructure defined by the module to other parts of your configuration.
The following files should not be distributed as part of your module:
- terraform.tfstate and terraform.tfstate.backup: These files contain your Terraform state, and are how Terraform keeps track of the relationship between your configuration and the infrastructure provisioned by it.
- .terraform: This directory contains the modules and plugins used to provision your infrastructure. These files are specific to a specific instance of Terraform when provisioning infrastructure, not the configuration of the infrastructure defined in
.tf
files. - *.tfvars: Since module input variables are set via arguments to the module block in your configuration, you don't need to distribute any
.tfvars
files with your module, unless you are also using it as a standalone Terraform configuration.
If you are tracking changes to your module in a version control system, such as git, you will want to configure your version control system to ignore these files.
Warning: Secret information, such as passwords or access keys, will become public if those files are committed to a public version control system such as GitHub.
More information is included on the official Terraform website.
Integration with application deployment
With our Terraform integration, an output value can be mapped to an environment variable from an application deployed in Kubernetes, or mapped as an input for other Terraform modules.
For example, let's assume a database in a cloud service has a random IP assigned to it. Bunnyshell can extract that IP in an output and then map that output to a variable from the web application component. When the environment is deployed, the Terraform module is applied and when the app is deployed, Bunnyshell injects the database IP in the variable from the web application, therefore linking the two.
The mechanism through which this happens is exportVariables.
Examples
To make it easier to understand how Bunnyshell integrates with Terraform, we included an example for how an AWS S3 bucket might be created using a simple Terraform module. You can find the full bunnyshell.yaml
on GitHub.
This example is also available in one of Bunnyshell's Templates.
-
kind: Terraform
name: s3-bucket
gitRepo: 'https://github.com/bunnyshell/demo-books.git'
gitBranch: terraform
gitApplicationPath: /backend/_terraform
runnerImage: 'hashicorp/terraform:1.5.1'
deploy:
- 'cd backend/_terraform'
- '/bns/helpers/terraform/get_managed_backend > zz_backend_override.tf'
- 'terraform init -input=false -no-color'
- 'terraform apply -var "bucket_name=demo-{{ env.unique }}" -input=false -auto-approve -no-color'
- 'BNS_TF_STATE_LIST=`terraform show -json`'
- 'BUCKET_NAME=`terraform output --raw s3_bucket_name`'
- 'BUCKET_REGION=`terraform output --raw s3_bucket_region`'
destroy:
- 'cd backend/_terraform'
- '/bns/helpers/terraform/get_managed_backend > zz_backend_override.tf'
- 'terraform init -input=false -no-color'
- 'terraform destroy -var "bucket_name=demo-{{ env.unique }}" -input=false -auto-approve -no-color'
start:
- '# you can trigger an external flow or alter resources consumed by resources when an Environment is started in Bunnyshell'
stop:
- '# you can trigger an external flow or alter resources consumed by resources when an Environment is stopped in Bunnyshell'
exportVariables:
- BUCKET_NAME
- BUCKET_REGION
environment:
AWS_ACCESS_KEY_ID: your-key
AWS_REGION: your-region
AWS_SECRET_ACCESS_KEY: your-secret
AWS Credentials
In order for the module to be able to create the S3 bucket, it needs AWS access with correct privileges. You can find the needed IAM permissions here.
The environment variables needed are:
AWS_ACCESS_KEY_ID: your-key
AWS_REGION: your-region
AWS_SECRET_ACCESS_KEY: your-secret
They can either be specified at Component level, in the
environment
attribute, or inherited from the environment level,environmentVariables
top-level attribute.
Let's understand what the module does, by analyzing each of its attributes.
Component attributes
gitRepo
The URL of the Git repository where the Terraform module is located. The repository will be cloned in the workdir of the runner image.
gitBranch
The branch where the Terraform module is located. This attribute is relative to the gitRepo
.
gitApplicationPath
The path to the application folder, relative to the branch. When provided, it will watch for changes only within this folder to trigger Auto-updates (if enabled) or to display that a new version of your application is available.
⚠️ The repository will be cloned entirely, not just the specified gitApplicationPath
.
runnerImage
This is where you specify the image where Bunnyshell runs the commands given by the user. It can be an image built specifically for the runner, or any other ready-made image.
You can build an image using a [DockerImage](components-docker-image)
component and use it as your runner.
Commands
In the Deploy, Start, Stop and Delete properties you can add any type of commands.
Note
Steps for the Deploy property are mandatory. The other properties can be left blank.
Storing the state
To be able to keep a track of create resources, the Terraform state must be persisted. Bunnyshell provides you with a backend to store the state in - credentials are scoped at the Organization level, meaning all Environments from all Projects within an Organization will have access to the same AWS S3 bucket.
You can use the Bunnyshell-provided backend or you can use your own.
- '/bns/helpers/terraform/get_managed_backend > zz_backend_override.tf'
Displaying Components' resources in the UI
For Bunnyshell to be able to discover the resources this Terraform module creates and read its output values, we need the Terraform state to be captured in an environment variable called BNS_TF_STATE_LIST
. This is the last line of the deploy scripts in the example.
- 'BNS_TF_STATE_LIST=`terraform show -json`'
Value Interpolation for Docker Images
If you are using Bunnyshell to build your images using Docker Image, so you can deploy them with Terraform outside Kubernetes, you can use value interpolation as exemplified below. NAME represents the component name of your Docker Image Component. Bunnyshell will replace it with the built image.
{{ components.NAME.image }}
: contains full image name, including the tag.
Example:nginx:latest
{{ components.dev_test_image.imageName }}
: contains only the image name.
Example:nginx
{{ components.dev_test_image.imageTag }}
: contains only the image tag.
Example:latest
Injecting Bunnyshell Variables in your resources
If you need to inject Bunnyshell environment variables in your resources, please refer to the Value Interpolation page.
exportVariables
Using this property you can export variables from Terraform modules to other components inside the environment. They are captured only during the deploy
and can be used after they reach the next interpolation context in the same deploy workflow, or in subsequent start/stop/destroy workflows.
Note
These are not environment variables, but variables exported from your deployment script.
Example: In the section presented above, under deploy, we defined the variables BUCKET_NAME
and BUCKET_REGION
.
deploy:
- 'cd backend/_terraform'
- '/bns/helpers/terraform/get_managed_backend > zz_backend_override.tf'
- 'terraform init -input=false -no-color'
- 'terraform apply -var "bucket_name=demo-{{ env.unique }}" -input=false -auto-approve -no-color'
- 'BNS_TF_STATE_LIST=`terraform show -json`'
- 'BUCKET_NAME=`terraform output --raw s3_bucket_name`'
- 'BUCKET_REGION=`terraform output --raw s3_bucket_region`'
Normally, these variables would only be available within the runner. But in our case, we are going to list them under exportedVariables
, so they can be used in other components as well.
exportVariables:
- BUCKET_NAME
- BUCKET_REGION
This way, after deployment Bunnyshell will store the variable (encrypted) in a database. The variable will be available for you to use in another component, if need be. You can use the exported variables in interpolation contexts using key with the format {{components.NAME.exported.VAR_NAME}}
:
{{components.s3-bucket.exported.BUCKET_NAME}}
{{components.s3-bucket.exported.BUCKET_REGION}}
Terraform Components also have Environment Variables. Environment Variables are injected in runnerImage
as Environment Variables. Usually, they are used in passing variables to Terraform modules
All Component, Environment and Project variables are inherited.
Resources & state
Resources are fully managed by Terraform using the state file, store in the backend of your choice.
Updated about 1 year ago