Thorn Tech Marketing Ad
Skip to main content
Version: 1.1.1

Terraform Template

Overview

You can now deploy StorageLink using Terraform.

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

Make sure you are subscribed to Storagelink in the Marketplace before deploying the Terraform template or else you will run into an error while creating the Virtual Machine.

The Marketplace links for each cloud are as listed below:

Running the template

This article contains two files:

  • slink-single-instance-default-vpc.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 marketplace AMI
  • 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

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
  • open_s3_permissions: Set this to true for full S3 permissions. Use false to restrict permissions to buckets with the naming convention slink-i-*
  • 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.

Terraform file contents

terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.27"
}
}
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: slink-<instance-id>."
default = false
}

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

variable "ami_map" {
type = map
default = {
us-east-1 = "ami-01d91fadf1bf4ec49"
us-gov-west-1 = "ami-0a1f6b8210ae30e20"
us-gov-east-1 = "ami-02cefae76b086885a"
us-east-2 = "ami-04ecc93823be4dcc6"
us-west-1 = "ami-045fd8e5c9c1b05d4"
us-west-2 = "ami-05f4502abc356ff14"
ca-central-1 = "ami-0a8665d4c6bb9e2c6"
eu-central-1 = "ami-0bb9ccf1e1c4aca16"
eu-west-1 = "ami-091f4403aff50eeb1"
eu-west-2 = "ami-00ba43b75058daf18"
eu-west-3 = "ami-04e0f730e38bcb77b"
ap-southeast-1 = "ami-0d23d9e3055d5dde8"
ap-southeast-2 = "ami-09788bbafd3cff0d1"
ap-southeast-3 = "ami-0a2d3450285fbd50d"
ap-south-1 = "ami-080c2387955cc249d"
ap-northeast-1 = "ami-02d3a4c3f406e2b72"
ap-northeast-2 = "ami-0a41660944b7473c3"
ap-northeast-3 = "ami-072f7e5bbddb98dfd"
sa-east-1 = "ami-0cbe9ed4ade983bf7"
eu-north-1 = "ami-0ba5a9335000a47ec"
ap-east-1 = "ami-0174075c13d3c5bc0"
me-south-1 = "ami-09b6b868621f1e67a"
af-south-1 = "ami-07ec275ec03c2bdaa"
eu-south-1 = "ami-066fbed6dc8671793"
}
}

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
vpc = true
}

resource "aws_instance" "storagelink" {
ami = lookup(var.ami_map, var.region)
instance_type = var.ec2_instance_size
key_name = var.key_name
security_groups = ["EC2-Security-group"]
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 = "iam_role"
managed_policy_arns = var.open_s3_permissions ? ["arn:${data.aws_partition.current.partition}:iam::aws:policy/AmazonS3FullAccess"] : []
assume_role_policy = jsonencode({
Version: "2012-10-17",
Statement: [
{
Action: "sts:AssumeRole",
Principal: {
Service: "ec2.amazonaws.com"
},
Effect: "Allow"
}
]
})
}

resource "aws_iam_instance_profile" "ec2_profile" {
name = "ec2_profile"
role = aws_iam_role.iam_role.name
}

resource "aws_iam_role_policy" "ec2_policy" {
name = "ec2_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:::slink-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 = "EC2-Security-group"
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"]
}
}

terraform.tfvars

key_name = "Rob"
region = "us-east-1"
admin_ip = "3.222.237.17/32"
open_s3_permissions = true