Thorn Tech Marketing Ad
Skip to main content
Version: 1.3.0

Terraform Template

TLDR

Purpose: Deploy a single StorageLink BYOL instance using Terraform (AWS, Azure, or Google Cloud)

Deployment:

  1. Create slink-single-instance.tf and terraform.tfvars
  2. Configure variables (region, admin IP, credentials)
  3. Run: terraform init && terraform apply

Resources Created:

  • AWS: EC2 instance, Security Group, Elastic IP, IAM role
  • Azure: VM, NSG, Public IP, Storage Account, VNet
  • Google: VM, Firewall, Static IP, VPC Network

Key Variables: admin_ip (your IP/32), SSH key or password, region

Prerequisites: AWS and Google Cloud: subscribe to the StorageLink BYOL listing. Azure: accept the StorageLink BYOL image terms once per subscription.

After Deployment: Create the admin account, then start the free 30-day trial in the admin UI — a BYOL instance starts unlicensed

Product: StorageLink by Thorn Technologies — cloud storage gateway for secure file sharing

Overview​

You can now deploy StorageLink using Terraform.

This article covers deploying a single instance of StorageLink version 1.3.0. The Terraform template is provided as an example, so feel free to further customize it for your business case.

Each template deploys the StorageLink BYOL listing, which has no marketplace software fee. Before you deploy, subscribe to the listing (AWS, Google Cloud) or accept its image terms (Azure), or else you will run into an error while creating the virtual machine. Each cloud's tab below has the exact steps.

A BYOL instance starts unlicensed. After you create the admin account, start the free 30-day trial in the admin UI, or activate a purchased license in Settings → License (see First Launch).

Running the template​

This article contains two files:

  • slink-single-instance.tf
  • terraform.tfvars

Create these two files on your workstation, using the file contents at the bottom of this page. Make adjustments to the terraform.tfvars file. Then, run the following commands:

terraform init
terraform plan

When you are ready to deploy the template, run:

terraform apply

How does it work​

This article contains a main Terraform template named:

slink-single-instance.tf

This template provisions the following resources:

  • EC2 instance: This server is based on the StorageLink BYOL marketplace AMI, launched into your default VPC
  • EC2 Security Group: Allows TCP 80 and 443 from anywhere, but locks down admin port 22 to a single IP
  • Elastic IP: Static IP that retains the IP after a reboot
  • IAM role: Grants the EC2 instance access to S3

Before you deploy, subscribe to StorageLink BYOL on AWS Marketplace: click View purchase options, select the Unit dimension (License units, $0.00), and click Subscribe. Subscribing is free — there is no software charge on the BYOL listing. Without the subscription, terraform apply fails with OptInRequired when it launches the instance.

There's also another file that contains variables:

terraform.tfvars

Since this file is named terraform.tfvars, it will be automatically used without having to run:

terraform -var-file terraform.tfvars

You can configure the following variables:

  • key_name: Specify the name of your EC2 key pair
  • region: Specify your current region
  • admin_ip: Get your workstation's public IP from checkip.dyndns.org. Append /32 to specify a single IP range
  • name_prefix: Optional. Defaults to storagelink. Prefix for the IAM role, instance profile, IAM policy, and security group names. IAM names are global to your AWS account, so use a different prefix for each deployment
  • open_s3_permissions: Optional. Defaults to false, which restricts permissions to buckets named storagelink-i-* (StorageLink creates storagelink-i-<instance-id> as its default bucket). Set this to true for full S3 permissions, for example to connect buckets with other names
  • image_id: Optional. Defaults to the StorageLink BYOL 1.3.0.1 AMI alias /aws/service/marketplace/prod-7y2el53dcmt24/1.3.0.1. To deploy the pay-as-you-go Standard listing instead, subscribe to StorageLink on AWS Marketplace and set /aws/service/marketplace/prod-6fb6mek7ta6vi/1.3.0; a Standard instance is limited to 10 StorageLink users
  • aws_profile: Optional. Specify an AWS CLI profile if not using the default profile.
  • ec2_instance_size: Optional. Defaults to t3.medium. Use this to override the EC2 instance size.
  • disk_volume_size: Optional. Defaults to 32. Use this to override the EC2 disk size, in GB.

When the deployment finishes, Terraform prints the instance's public_ip. Browse to https://<public_ip> and complete the First Launch Experience: the template does not create an admin account, so create it on the Welcome page (Create your account), then sign in with it. StorageLink creates its S3 bucket, storagelink-i-<instance-id>, when you create the admin account and uses it for the root folder. A BYOL instance starts unlicensed — start the free 30-day trial from the admin UI, then book a 15-minute call when you are ready to purchase a license.

terraform destroy removes the instance, Elastic IP, IAM role, and security group. It does not remove the storagelink-i-<instance-id> bucket, which holds your files; empty and delete it if you no longer need them.

Terraform file contents​

terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
required_version = ">= 0.14.9"
}

provider "aws" {
region = var.region
profile = var.aws_profile != "" ? var.aws_profile : null
}

data "aws_partition" "current" {}

variable "open_s3_permissions" {
type = bool
description = "Set this to true to allow full S3 access to support multiple buckets. Otherwise, S3 permissions are limited to our default bucket naming convention: storagelink-i-<instance-id>."
default = false
}

variable "region" {
type = string
description = "This is the AWS region"
}

variable "image_id" {
type = string
default = "/aws/service/marketplace/prod-7y2el53dcmt24/1.3.0.1"
description = "SSM alias of the StorageLink Marketplace AMI. Default: StorageLink BYOL 1.3.0.1 (/aws/service/marketplace/prod-7y2el53dcmt24/1.3.0.1). For the pay-as-you-go Standard listing use /aws/service/marketplace/prod-6fb6mek7ta6vi/1.3.0."

validation {
condition = contains(["/aws/service/marketplace/prod-7y2el53dcmt24/1.3.0.1", "/aws/service/marketplace/prod-6fb6mek7ta6vi/1.3.0"], var.image_id)
error_message = "Use /aws/service/marketplace/prod-7y2el53dcmt24/1.3.0.1 (StorageLink BYOL) or /aws/service/marketplace/prod-6fb6mek7ta6vi/1.3.0 (Standard, pay-as-you-go)."
}
}

data "aws_ssm_parameter" "marketplace_ami" {
name = var.image_id
}

variable "name_prefix" {
type = string
default = "storagelink"
description = "Prefix for the IAM role, instance profile, policy and security group names. Use a different prefix for each deployment in the same account."
}

variable "aws_profile" {
type = string
default = "default"
description = "Optional: specify an AWS profile"
}

variable "key_name" {
type = string
description = "Make sure you have access to this EC2 key pair. Otherwise, create a new key pair before proceeding."
}

variable "ec2_instance_size" {
type = string
description = "EC2 instance size. Recommended: t3.medium for testing, m5.large for production."
default = "t3.medium"
}

variable "disk_volume_size" {
type = number
description = "Disk volume size in GB. Must be at least 8."
default = 32
}

variable "admin_ip" {
type = string
description = "Public IP address range for SSH and web access. Use a CIDR range to restrict access. To get your local machine's IP, see http://checkip.dyndns.org/. (Remember to append /32 for a single IP e.g. 12.34.56.78/32) For security reasons, do not use 0.0.0.0/0."
validation {
condition = can(regex("([1-9]\\d{0,2})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/([1-9]\\d{0,1})",var.admin_ip))
error_message = "Must be a valid IP CIDR range in the form of x.x.x.x/x. Do not use 0.0.0.0/0."
}
}

resource "aws_eip" "eip" {
instance = aws_instance.storagelink.id
domain = "vpc"
}

resource "aws_instance" "storagelink" {
ami = nonsensitive(data.aws_ssm_parameter.marketplace_ami.value)
instance_type = var.ec2_instance_size
key_name = var.key_name
vpc_security_group_ids = [aws_security_group.sg.id]
iam_instance_profile = aws_iam_instance_profile.ec2_profile.name
root_block_device {
volume_size = var.disk_volume_size
volume_type = "gp2"
encrypted = true
}
tags = {
Name = "storagelink"
}
}

resource "aws_iam_role" "iam_role" {
name = "${var.name_prefix}-role"
assume_role_policy = jsonencode({
Version: "2012-10-17",
Statement: [
{
Action: "sts:AssumeRole",
Principal: {
Service: "ec2.amazonaws.com"
},
Effect: "Allow"
}
]
})
}

resource "aws_iam_role_policy_attachment" "s3_full" {
count = var.open_s3_permissions ? 1 : 0
role = aws_iam_role.iam_role.name
policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/AmazonS3FullAccess"
}

resource "aws_iam_instance_profile" "ec2_profile" {
name = "${var.name_prefix}-instance-profile"
role = aws_iam_role.iam_role.name
}

resource "aws_iam_role_policy" "ec2_policy" {
name = "${var.name_prefix}-policy"
role = aws_iam_role.iam_role.id
policy = jsonencode({
"Version": "2012-10-17",
"Statement": [{
Action: "s3:*",
Effect: "Allow",
Resource: "arn:${data.aws_partition.current.partition}:s3:::storagelink-i-*"
}, {
Action: [
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams",
"logs:CreateLogGroup",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeInstances",
"ec2:DescribeTags",
"s3:ListAllMyBuckets",
"cloudformation:DescribeStacks",
"cloudformation:ListStackResources"
],
Effect: "Allow",
Resource: "*"
}]
})
}

resource "aws_security_group" "sg" {
name = "${var.name_prefix}-sg"
description = "EC2 Security Group"
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [var.admin_ip]
}
egress {
from_port = 0
protocol = "tcp"
to_port = 65535
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
protocol = "udp"
to_port = 65535
cidr_blocks = ["0.0.0.0/0"]
}
}

output "public_ip" {
value = aws_eip.eip.public_ip
description = "Static public IP of the StorageLink instance — browse to https://<public_ip>"
}

terraform.tfvars​

key_name    = "my-keypair"
region = "us-east-1"
admin_ip = "1.2.3.4/32"
name_prefix = "storagelink"