Home Creating a Storage Gateway in AWS
Post
Cancel

Creating a Storage Gateway in AWS

So what is a Storge Gateway?

A Storage Gateway it just a way to connect AWS storage in a traditional way e.g. NFS, SMB etc.

Lets build one with Terraform

A fully working envriment with a Storage Gateway can be found here

So a Storage Gateway is built up of the following

  • A instance with a dedicated 150GB cahce drive
  • A role
  • A security group
  • S3 bucket (in this example)
  • S3 policy

The Instance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// NOTE: the varable for the instance ami used here will be stored in a datasource.tf file.

  resource "aws_instance" "dev_gateway" {
  instance_type          = "m5.xlarge"
  ami                    = data.aws_ami.server_ami_gw.id
  key_name               = aws_key_pair.clouddev_auth.id
  vpc_security_group_ids = [aws_security_group.main_gateway_sg.id]
  iam_instance_profile   = aws_iam_instance_profile.dev_profile.name
  subnet_id              = aws_subnet.main_public_subnet.id

  root_block_device {
    volume_size = 80
    volume_type = "gp3"
  }

  tags = {
    Name = "dev-gateway"
  }
  }


// NOTE: This is where you will define the disk used for the cache.

resource "aws_ebs_volume" "dev-cache" {
  availability_zone = "eu-west-2a"
  size = 150
  type = "gp3"
  encrypted = true

  tags = {
    Name = "dev-cache"
  }
    depends_on = [
    aws_instance.dev_gateway
  ]
}

// NOTE: This is where you will attck the disk to the new instance. 

resource "aws_volume_attachment" "ebs_att" {
  device_name = "/dev/xvds"
  volume_id   = aws_ebs_volume.dev-cache.id
  instance_id = aws_instance.dev_gateway.id
}

data "aws_storagegateway_local_disk" "example" {
  disk_node   = "/dev/xvds"
  gateway_arn = aws_storagegateway_gateway.g1.arn
}

resource "aws_storagegateway_cache" "dev-cache" {
  disk_id     = data.aws_storagegateway_local_disk.example.id
  gateway_arn = aws_storagegateway_gateway.g1.arn
}

The varable for the instance ami will look like this:

The ami that is being used has been built by AWS.

1
2
3
4
5
6
7
8
9
10
data "aws_ami" "server_ami_gw" {
  most_recent = true
  owners      = ["029176880048"]

  filter {
    name   = "name"
    values = ["aws-storage-gateway-*"]
  }
}

If you are building this in one .tf file just add the above data source for it run.

Security Group

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// NOTE: Ingress / Egress rules taken from AWS:
// https://docs.aws.amazon.com/storagegateway/latest/userguide/Resource_Ports.html
resource "aws_security_group" "main_gateway_sg" {
  name        = "dev-gateway"
  description = "Security Group for NFS File Gateway."
  vpc_id      = aws_vpc.mainvpc.id

  // Activation

  ingress {
    protocol    = "tcp"
    from_port   = 80
    to_port     = 80
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    protocol    = "tcp"
    from_port   = 443
    to_port     = 443
    cidr_blocks = ["0.0.0.0/0"]
  }

  // NFS

  ingress {
    protocol    = "tcp"
    from_port   = 20048
    to_port     = 20048
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    protocol    = "udp"
    from_port   = 20048
    to_port     = 20048
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    protocol    = "tcp"
    from_port   = 111
    to_port     = 111
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    protocol    = "udp"
    from_port   = 111
    to_port     = 111
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    protocol    = "tcp"
    from_port   = 2049
    to_port     = 2049
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    protocol    = "udp"
    from_port   = 2049
    to_port     = 2049
    cidr_blocks = ["0.0.0.0/0"]
  }

  // DNS

  ingress {
    protocol    = "tcp"
    from_port   = 53
    to_port     = 53
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    protocol    = "udp"
    from_port   = 53
    to_port     = 53
    cidr_blocks = ["0.0.0.0/0"]
  }

  // Allow all egress

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

Lets configure the gateway,NFS share & add the S3 policy.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
resource "aws_storagegateway_gateway" "g1" {
  gateway_name       = var.gateway_name
  gateway_timezone   = var.gateway_timezone
  gateway_type       = "FILE_S3"
  gateway_ip_address = aws_instance.dev_gateway.public_ip

  depends_on = [
    aws_instance.dev_gateway
  ]

}

resource "aws_storagegateway_nfs_file_share" "file_share" {
  client_list  = ["0.0.0.0/0"]
  gateway_arn  = aws_storagegateway_gateway.g1.arn
  location_arn = aws_s3_bucket.gate_buc.arn
  role_arn     = aws_iam_role.role.arn
}

resource "aws_iam_policy" "policy" {
  name        = "S3_policy"
  description = "Providing limited S3 powers"

  # Terraform's "jsonencode" function converts a
  # Terraform expression result to valid JSON syntax.
  policy = jsonencode({
      "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:GetAccelerateConfiguration",
                "s3:GetBucketLocation",
                "s3:GetBucketVersioning",
                "s3:ListBucket",
                "s3:ListBucketVersions",
                "s3:ListBucketMultipartUploads"
            ],
            "Resource": "arn:aws:s3:::${var.bucket_name}",
            "Effect": "Allow"
        },
        {
            "Action": [
                "s3:AbortMultipartUpload",
                "s3:DeleteObject",
                "s3:DeleteObjectVersion",
                "s3:GetObject",
                "s3:GetObjectAcl",
                "s3:GetObjectVersion",
                "s3:ListMultipartUploadParts",
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::${var.bucket_name}/*",
            "Effect": "Allow"
        }
    ]
  })
}

Finally lets create our S3 butcket

1
2
3
4
5
6
7
resource "aws_s3_bucket" "gate_buc" {
  bucket = var.bucket_name

  tags = {
    Name        = "My bucket"
    Environment = "Dev"
  }

And that’s it, run your terraform apply. You should now have a storage gaetway instance in your EC2 console and your share will be available from the Storage Gateway console with the command you need to mount it. Make sure you read the AWS Docs and the Terraform Docs

Is this the best way to access cloud storage?

No, I don’t think it is for the following reasons:

  • This solution will be charging you for the instance, ebs volume, S3 bucket and the pulic IP etc.
  • There is a easier way to mount cloud storage on prem and within the cloud environment……Rclone
This post is licensed under CC BY 4.0 by the author.