diff --git a/services/terraform/.gitignore b/services/terraform/.gitignore
new file mode 100644
--- /dev/null
+++ b/services/terraform/.gitignore
@@ -0,0 +1,34 @@
+# Local .terraform directories
+**/.terraform/*
+
+# .tfstate files
+*.tfstate
+*.tfstate.*
+
+# Crash log files
+crash.log
+crash.*.log
+
+# Exclude all .tfvars files, which are likely to contain sensitive data, such as
+# password, private keys, and other secrets. These should not be part of version 
+# control as they are data points which are potentially sensitive and subject 
+# to change depending on the environment.
+*.tfvars
+*.tfvars.json
+
+# Ignore override files as they are usually used to override resources locally and so
+# are not checked in
+override.tf
+override.tf.json
+*_override.tf
+*_override.tf.json
+
+# Include override files you do wish to add to version control using negated pattern
+# !example_override.tf
+
+# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
+# example: *tfplan*
+
+# Ignore CLI configuration files
+.terraformrc
+terraform.rc
diff --git a/services/terraform/.terraform.lock.hcl b/services/terraform/.terraform.lock.hcl
new file mode 100644
--- /dev/null
+++ b/services/terraform/.terraform.lock.hcl
@@ -0,0 +1,22 @@
+# This file is maintained automatically by "terraform init".
+# Manual edits may be lost in future updates.
+
+provider "registry.terraform.io/hashicorp/aws" {
+  version     = "4.9.0"
+  constraints = "~> 4.9.0"
+  hashes = [
+    "h1:OWIIlbMZl/iQ8qR1U7Co3sGjNHL1HJtgNRnnV1kXNuI=",
+    "zh:084b83aef3335ad4f5e4b8323c6fe43c1ff55e17a7647c6a5cad6af519f72b42",
+    "zh:132e47ce69f14de4523b84b213cedf7173398acda14245b1ffe7747aac50f050",
+    "zh:2068baef7dfce3613f3b4f27314175e971f8db68d9cde9ec30b5659f80c68c6c",
+    "zh:63c6f489683d5f1ac55e82a0df387143ed22701d5f22c109a4d5c9924dd4e437",
+    "zh:8115fd21965954fa4568c09331e05bb29da967fab8d077419aed09954378e216",
+    "zh:8efdc95fde108f777ed9c79ae25dc17aea9771903250f5c5c8a4c726b90a345f",
+    "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
+    "zh:9d42a7bc34d84b70c1d1bcc215cabd63abbcbd0352b70bd84da6c3916634932f",
+    "zh:aacbcceb241aa475888c0869e87593182edeced3170c76a0c960dd9c905df449",
+    "zh:c7fe7904511052e4102870256819a1917177572cf684f0611ebf767f9c1fbaa8",
+    "zh:c8e07c3424663d1d0e7e32f4ade8099c19f6326d37c6da98104d90c986ff66fc",
+    "zh:e47cafbd38b56ef14fd8d727b4ffea847c166b1c684f585ee5fb78983b537248",
+  ]
+}
diff --git a/services/terraform/dynamodb-test.tf b/services/terraform/dynamodb-test.tf
new file mode 100644
--- /dev/null
+++ b/services/terraform/dynamodb-test.tf
@@ -0,0 +1,87 @@
+resource "aws_dynamodb_table" "backup-service-backup-test" {
+  name           = "backup-service-backup-test"
+  hash_key       = "userID"
+  range_key      = "backupID"
+  write_capacity = 10
+  read_capacity  = 10
+
+  attribute {
+    name = "userID"
+    type = "S"
+  }
+
+  attribute {
+    name = "backupID"
+    type = "S"
+  }
+
+  attribute {
+    name = "created"
+    type = "S"
+  }
+
+  global_secondary_index {
+    name               = "userID-created-index"
+    hash_key           = "userID"
+    range_key          = "created"
+    write_capacity     = 10
+    read_capacity      = 10
+    projection_type    = "INCLUDE"
+    non_key_attributes = ["recoveryData"]
+  }
+}
+
+resource "aws_dynamodb_table" "backup-service-log-test" {
+  name           = "backup-service-log-test"
+  hash_key       = "backupID"
+  range_key      = "logID"
+  write_capacity = 10
+  read_capacity  = 10
+
+  attribute {
+    name = "backupID"
+    type = "S"
+  }
+
+  attribute {
+    name = "logID"
+    type = "S"
+  }
+}
+
+resource "aws_dynamodb_table" "blob-service-blob-test" {
+  name           = "blob-service-blob-test"
+  hash_key       = "blobHash"
+  write_capacity = 10
+  read_capacity  = 10
+
+  attribute {
+    name = "blobHash"
+    type = "S"
+  }
+}
+
+resource "aws_dynamodb_table" "blob-service-reverse-index-test" {
+  name           = "blob-service-reverse-index-test"
+  hash_key       = "holder"
+  write_capacity = 10
+  read_capacity  = 10
+
+  attribute {
+    name = "holder"
+    type = "S"
+  }
+
+  attribute {
+    name = "blobHash"
+    type = "S"
+  }
+
+  global_secondary_index {
+    name               = "blobHash-index"
+    hash_key           = "blobHash"
+    write_capacity     = 10
+    read_capacity      = 10
+    projection_type    = "ALL"
+  }
+}
diff --git a/services/terraform/dynamodb.tf b/services/terraform/dynamodb.tf
new file mode 100644
--- /dev/null
+++ b/services/terraform/dynamodb.tf
@@ -0,0 +1,87 @@
+resource "aws_dynamodb_table" "backup-service-backup" {
+  name           = "backup-service-backup"
+  hash_key       = "userID"
+  range_key      = "backupID"
+  write_capacity = 10
+  read_capacity  = 10
+
+  attribute {
+    name = "userID"
+    type = "S"
+  }
+
+  attribute {
+    name = "backupID"
+    type = "S"
+  }
+
+  attribute {
+    name = "created"
+    type = "S"
+  }
+
+  global_secondary_index {
+    name               = "userID-created-index"
+    hash_key           = "userID"
+    range_key          = "created"
+    write_capacity     = 10
+    read_capacity      = 10
+    projection_type    = "INCLUDE"
+    non_key_attributes = ["recoveryData"]
+  }
+}
+
+resource "aws_dynamodb_table" "backup-service-log" {
+  name           = "backup-service-log"
+  hash_key       = "backupID"
+  range_key      = "logID"
+  write_capacity = 10
+  read_capacity  = 10
+
+  attribute {
+    name = "backupID"
+    type = "S"
+  }
+
+  attribute {
+    name = "logID"
+    type = "S"
+  }
+}
+
+resource "aws_dynamodb_table" "blob-service-blob" {
+  name           = "blob-service-blob"
+  hash_key       = "blobHash"
+  write_capacity = 10
+  read_capacity  = 10
+
+  attribute {
+    name = "blobHash"
+    type = "S"
+  }
+}
+
+resource "aws_dynamodb_table" "blob-service-reverse-index" {
+  name           = "blob-service-reverse-index"
+  hash_key       = "holder"
+  write_capacity = 10
+  read_capacity  = 10
+
+  attribute {
+    name = "holder"
+    type = "S"
+  }
+
+  attribute {
+    name = "blobHash"
+    type = "S"
+  }
+
+  global_secondary_index {
+    name               = "blobHash-index"
+    hash_key           = "blobHash"
+    write_capacity     = 10
+    read_capacity      = 10
+    projection_type    = "ALL"
+  }
+}
diff --git a/services/terraform/localstack.tf b/services/terraform/localstack.tf
new file mode 100644
--- /dev/null
+++ b/services/terraform/localstack.tf
@@ -0,0 +1,18 @@
+variable "HOST" {
+  type = string
+}
+
+provider "aws" {
+  region                      = "us-east-2"
+  access_key                  = "fake"
+  secret_key                  = "fake"
+  skip_credentials_validation = true
+  skip_metadata_api_check     = true
+  skip_requesting_account_id  = true
+  s3_use_path_style           = true
+
+  endpoints {
+    dynamodb = var.HOST
+    s3 = var.HOST
+  }
+}
diff --git a/services/terraform/providers.tf b/services/terraform/providers.tf
new file mode 100644
--- /dev/null
+++ b/services/terraform/providers.tf
@@ -0,0 +1,8 @@
+terraform {
+  required_providers {
+    aws = {
+      source  = "hashicorp/aws"
+      version = "~> 4.9.0"
+    }
+  }
+}
diff --git a/services/terraform/run.sh b/services/terraform/run.sh
new file mode 100755
--- /dev/null
+++ b/services/terraform/run.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -e
+
+terraform init
+
+terraform apply -auto-approve -var="HOST=http://localhost:4566"
diff --git a/services/terraform/s3.tf b/services/terraform/s3.tf
new file mode 100644
--- /dev/null
+++ b/services/terraform/s3.tf
@@ -0,0 +1,11 @@
+variable "s3_bucket_names" {
+  type = list
+  default = [
+    "commapp-blob",
+  ]
+}
+
+resource "aws_s3_bucket" "comm_buckets" {
+  count         = length(var.s3_bucket_names)
+  bucket        = var.s3_bucket_names[count.index]
+}