21700: Install Bundler system-wide in Rails postinst
[arvados.git] / tools / salt-install / terraform / aws / vpc / main.tf
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: CC-BY-SA-3.0
4
5 terraform {
6   required_version = "~> 1.3.0"
7   required_providers {
8     aws = {
9       source = "hashicorp/aws"
10       version = "~> 4.38.0"
11     }
12   }
13 }
14
15 provider "aws" {
16   region = var.region_name
17   default_tags {
18     tags = merge(var.custom_tags, {
19       Arvados = var.cluster_name
20       Terraform = true
21     })
22   }
23 }
24
25 resource "aws_vpc" "arvados_vpc" {
26   count = var.vpc_id == "" ? 1 : 0
27   cidr_block = "10.1.0.0/16"
28   enable_dns_hostnames = true
29   enable_dns_support = true
30
31   lifecycle {
32     precondition {
33       condition = (var.sg_id == "")
34       error_message = "vpc_id should be set if sg_id is also set"
35     }
36   }
37 }
38 resource "aws_subnet" "public_subnet" {
39   count = var.public_subnet_id == "" ? 1 : 0
40   vpc_id = local.arvados_vpc_id
41   availability_zone = local.availability_zone
42   cidr_block = "10.1.1.0/24"
43
44   lifecycle {
45     precondition {
46       condition = (var.vpc_id == "")
47       error_message = "public_subnet_id should be set if vpc_id is also set"
48     }
49   }
50 }
51 resource "aws_subnet" "private_subnet" {
52   count = var.private_subnet_id == "" ? 1 : 0
53   vpc_id = local.arvados_vpc_id
54   availability_zone = local.availability_zone
55   cidr_block = "10.1.2.0/24"
56
57   lifecycle {
58     precondition {
59       condition = (var.vpc_id == "")
60       error_message = "private_subnet_id should be set if vpc_id is also set"
61     }
62   }
63 }
64
65 #
66 # VPC S3 access
67 #
68 resource "aws_vpc_endpoint" "s3" {
69   count = var.vpc_id == "" ? 1 : 0
70   vpc_id = local.arvados_vpc_id
71   service_name = "com.amazonaws.${var.region_name}.s3"
72 }
73 resource "aws_vpc_endpoint_route_table_association" "compute_s3_route" {
74   count = var.private_subnet_id == "" ? 1 : 0
75   vpc_endpoint_id = aws_vpc_endpoint.s3[0].id
76   route_table_id = aws_route_table.private_subnet_rt[0].id
77 }
78
79 #
80 # Internet access for Public IP instances
81 #
82 resource "aws_internet_gateway" "internet_gw" {
83   count = var.vpc_id == "" ? 1 : 0
84   vpc_id = local.arvados_vpc_id
85 }
86 resource "aws_eip" "arvados_eip" {
87   for_each = toset(local.public_hosts)
88   depends_on = [
89     aws_internet_gateway.internet_gw
90   ]
91 }
92 resource "aws_route_table" "public_subnet_rt" {
93   count = var.public_subnet_id == "" ? 1 : 0
94   vpc_id = local.arvados_vpc_id
95   route {
96     cidr_block = "0.0.0.0/0"
97     gateway_id = aws_internet_gateway.internet_gw[0].id
98   }
99 }
100 resource "aws_route_table_association" "public_subnet_assoc" {
101   count = var.public_subnet_id == "" ? 1 : 0
102   subnet_id = aws_subnet.public_subnet[0].id
103   route_table_id = aws_route_table.public_subnet_rt[0].id
104 }
105
106 #
107 # Internet access for Private IP instances
108 #
109 resource "aws_eip" "nat_gw_eip" {
110   count = var.private_subnet_id == "" ? 1 : 0
111   depends_on = [
112     aws_internet_gateway.internet_gw[0]
113   ]
114 }
115 resource "aws_nat_gateway" "nat_gw" {
116   count = var.private_subnet_id == "" ? 1 : 0
117   # A NAT gateway should be placed on a subnet with an internet gateway
118   subnet_id = aws_subnet.public_subnet[0].id
119   allocation_id = aws_eip.nat_gw_eip[0].id
120 }
121 resource "aws_route_table" "private_subnet_rt" {
122   count = var.private_subnet_id == "" ? 1 : 0
123   vpc_id = local.arvados_vpc_id
124   route {
125     cidr_block = "0.0.0.0/0"
126     nat_gateway_id = aws_nat_gateway.nat_gw[0].id
127   }
128 }
129 resource "aws_route_table_association" "private_subnet_assoc" {
130   count = var.private_subnet_id == "" ? 1 : 0
131   subnet_id = aws_subnet.private_subnet[0].id
132   route_table_id = aws_route_table.private_subnet_rt[0].id
133 }
134
135 resource "aws_security_group" "arvados_sg" {
136   name = "arvados_sg"
137   count = var.sg_id == "" ? 1 : 0
138   vpc_id = aws_vpc.arvados_vpc[0].id
139
140   lifecycle {
141     precondition {
142       condition = (var.vpc_id == "")
143       error_message = "sg_id should be set if vpc_id is set"
144     }
145   }
146
147   dynamic "ingress" {
148     for_each = local.allowed_ports
149     content {
150       description = "Ingress rule for ${ingress.key}"
151       from_port = "${ingress.value}"
152       to_port = "${ingress.value}"
153       protocol = "tcp"
154       cidr_blocks = ["0.0.0.0/0"]
155       ipv6_cidr_blocks = ["::/0"]
156     }
157   }
158   # Allows communication between nodes in the VPC
159   ingress {
160     from_port = 0
161     to_port = 0
162     protocol = "-1"
163     cidr_blocks = [ aws_vpc.arvados_vpc[0].cidr_block ]
164   }
165   # Even though AWS auto-creates an "allow all" egress rule,
166   # Terraform deletes it, so we add it explicitly.
167   egress {
168     from_port = 0
169     to_port = 0
170     protocol = "-1"
171     cidr_blocks = ["0.0.0.0/0"]
172     ipv6_cidr_blocks = ["::/0"]
173   }
174 }
175
176 #
177 # Route53 split-horizon DNS zones
178 #
179
180 # PUBLIC DNS
181 resource "aws_route53_zone" "public_zone" {
182   count = var.private_only ? 0 : 1
183   name = var.domain_name
184 }
185 resource "aws_route53_record" "public_a_record" {
186   zone_id = try(local.route53_public_zone.id, "")
187   for_each = local.public_ip
188   name = each.key
189   type = "A"
190   ttl = 300
191   records = [ each.value ]
192 }
193 resource "aws_route53_record" "main_a_record" {
194   count = var.private_only ? 0 : 1
195   zone_id = try(local.route53_public_zone.id, "")
196   name = ""
197   type = "A"
198   ttl = 300
199   records = [ local.public_ip["controller"] ]
200 }
201 resource "aws_route53_record" "public_cname_record" {
202   zone_id = try(local.route53_public_zone.id, "")
203   for_each = {
204     for i in local.cname_by_host: i.record =>
205       "${i.cname}.${var.domain_name}"
206     if var.private_only == false
207   }
208   name = each.key
209   type = "CNAME"
210   ttl = 300
211   records = [ each.value ]
212 }
213
214 # PRIVATE DNS
215 resource "aws_route53_zone" "private_zone" {
216   name = var.domain_name
217   vpc {
218     vpc_id = local.arvados_vpc_id
219   }
220 }
221 resource "aws_route53_record" "private_a_record" {
222   zone_id = aws_route53_zone.private_zone.id
223   for_each = local.private_ip
224   name = each.key
225   type = "A"
226   ttl = 300
227   records = [ each.value ]
228 }
229 resource "aws_route53_record" "private_main_a_record" {
230   zone_id = aws_route53_zone.private_zone.id
231   name = ""
232   type = "A"
233   ttl = 300
234   records = [ local.private_ip["controller"] ]
235 }
236 resource "aws_route53_record" "private_cname_record" {
237   zone_id = aws_route53_zone.private_zone.id
238   for_each = {for i in local.cname_by_host: i.record => "${i.cname}.${var.domain_name}" }
239   name = each.key
240   type = "CNAME"
241   ttl = 300
242   records = [ each.value ]
243 }
244
245 #
246 # Route53's credentials for Let's Encrypt
247 #
248 resource "aws_iam_user" "letsencrypt" {
249   count = var.private_only ? 0 : 1
250   name = "${var.cluster_name}-letsencrypt"
251   path = "/"
252 }
253
254 resource "aws_iam_access_key" "letsencrypt" {
255   count = var.private_only ? 0 : 1
256   user = local.iam_user_letsencrypt.name
257 }
258 resource "aws_iam_user_policy" "letsencrypt_iam_policy" {
259   count = var.private_only ? 0 : 1
260   name = "${var.cluster_name}-letsencrypt_iam_policy"
261   user = local.iam_user_letsencrypt.name
262   policy = jsonencode({
263     "Version": "2012-10-17",
264     "Statement": [{
265       "Effect": "Allow",
266       "Action": [
267         "route53:ListHostedZones",
268         "route53:GetChange"
269       ],
270       "Resource": [
271           "*"
272       ]
273     },{
274       "Effect" : "Allow",
275       "Action" : [
276         "route53:ChangeResourceRecordSets"
277       ],
278       "Resource" : [
279         "arn:aws:route53:::hostedzone/${local.route53_public_zone.id}"
280       ]
281     }]
282   })
283 }
284