Merge branch '19954-permission-dedup-doc'
[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_providers {
7     aws = {
8       source = "hashicorp/aws"
9     }
10   }
11 }
12
13 provider "aws" {
14   region = var.region_name
15   default_tags {
16     tags = {
17       Arvados = var.cluster_name
18     }
19   }
20 }
21
22 resource "aws_vpc" "arvados_vpc" {
23   cidr_block = "10.1.0.0/16"
24   enable_dns_hostnames = true
25   enable_dns_support = true
26 }
27 resource "aws_subnet" "arvados_subnet" {
28   vpc_id = aws_vpc.arvados_vpc.id
29   availability_zone = local.availability_zone
30   cidr_block = "10.1.1.0/24"
31 }
32 resource "aws_subnet" "compute_subnet" {
33   vpc_id = aws_vpc.arvados_vpc.id
34   availability_zone = local.availability_zone
35   cidr_block = "10.1.2.0/24"
36 }
37
38 #
39 # VPC S3 access
40 #
41 resource "aws_vpc_endpoint" "s3" {
42   vpc_id = aws_vpc.arvados_vpc.id
43   service_name = "com.amazonaws.${var.region_name}.s3"
44 }
45 resource "aws_vpc_endpoint_route_table_association" "arvados_s3_route" {
46   vpc_endpoint_id = aws_vpc_endpoint.s3.id
47   route_table_id = aws_route_table.arvados_subnet_rt.id
48 }
49 resource "aws_vpc_endpoint_route_table_association" "compute_s3_route" {
50   vpc_endpoint_id = aws_vpc_endpoint.s3.id
51   route_table_id = aws_route_table.compute_subnet_rt.id
52 }
53
54 #
55 # Internet access for Public IP instances
56 #
57 resource "aws_internet_gateway" "arvados_gw" {
58   vpc_id = aws_vpc.arvados_vpc.id
59 }
60 resource "aws_eip" "arvados_eip" {
61   for_each = toset(local.hostnames)
62   depends_on = [
63     aws_internet_gateway.arvados_gw
64   ]
65 }
66 resource "aws_route_table" "arvados_subnet_rt" {
67   vpc_id = aws_vpc.arvados_vpc.id
68   route {
69     cidr_block = "0.0.0.0/0"
70     gateway_id = aws_internet_gateway.arvados_gw.id
71   }
72 }
73 resource "aws_route_table_association" "arvados_subnet_assoc" {
74   subnet_id = aws_subnet.arvados_subnet.id
75   route_table_id = aws_route_table.arvados_subnet_rt.id
76 }
77
78 #
79 # Internet access for Private IP instances
80 #
81 resource "aws_eip" "compute_nat_gw_eip" {
82   depends_on = [
83     aws_internet_gateway.arvados_gw
84   ]
85 }
86 resource "aws_nat_gateway" "compute_nat_gw" {
87   # A NAT gateway should be placed on a subnet with an internet gateway
88   subnet_id = aws_subnet.arvados_subnet.id
89   allocation_id = aws_eip.compute_nat_gw_eip.id
90 }
91 resource "aws_route_table" "compute_subnet_rt" {
92   vpc_id = aws_vpc.arvados_vpc.id
93   route {
94     cidr_block = "0.0.0.0/0"
95     nat_gateway_id = aws_nat_gateway.compute_nat_gw.id
96   }
97 }
98 resource "aws_route_table_association" "compute_subnet_assoc" {
99   subnet_id = aws_subnet.compute_subnet.id
100   route_table_id = aws_route_table.compute_subnet_rt.id
101 }
102
103 resource "aws_security_group" "arvados_sg" {
104   name = "arvados_sg"
105   vpc_id = aws_vpc.arvados_vpc.id
106
107   dynamic "ingress" {
108     for_each = local.allowed_ports
109     content {
110       description = "Ingress rule for ${ingress.key}"
111       from_port = "${ingress.value}"
112       to_port = "${ingress.value}"
113       protocol = "tcp"
114       cidr_blocks = ["0.0.0.0/0"]
115       ipv6_cidr_blocks = ["::/0"]
116     }
117   }
118   # Allows communication between nodes in the VPC
119   ingress {
120     from_port = 0
121     to_port = 0
122     protocol = "-1"
123     cidr_blocks = [ aws_vpc.arvados_vpc.cidr_block ]
124   }
125   # Even though AWS auto-creates an "allow all" egress rule,
126   # Terraform deletes it, so we add it explicitly.
127   egress {
128     from_port = 0
129     to_port = 0
130     protocol = "-1"
131     cidr_blocks = ["0.0.0.0/0"]
132     ipv6_cidr_blocks = ["::/0"]
133   }
134 }
135
136 #
137 # Route53 split-horizon DNS zones
138 #
139
140 # PUBLIC DNS
141 resource "aws_route53_zone" "public_zone" {
142   name = local.arvados_dns_zone
143 }
144 resource "aws_route53_record" "public_a_record" {
145   zone_id = aws_route53_zone.public_zone.id
146   for_each = local.public_ip
147   name = each.key
148   type = "A"
149   ttl = 300
150   records = [ each.value ]
151 }
152 resource "aws_route53_record" "main_a_record" {
153   zone_id = aws_route53_zone.public_zone.id
154   name = ""
155   type = "A"
156   ttl = 300
157   records = [ local.public_ip["controller"] ]
158 }
159 resource "aws_route53_record" "public_cname_record" {
160   zone_id = aws_route53_zone.public_zone.id
161   for_each = {for i in local.cname_by_host: i.record => "${i.cname}.${local.arvados_dns_zone}" }
162   name = each.key
163   type = "CNAME"
164   ttl = 300
165   records = [ each.value ]
166 }
167
168 # PRIVATE DNS
169 resource "aws_route53_zone" "private_zone" {
170   name = local.arvados_dns_zone
171   vpc {
172     vpc_id = aws_vpc.arvados_vpc.id
173   }
174 }
175 resource "aws_route53_record" "private_a_record" {
176   zone_id = aws_route53_zone.private_zone.id
177   for_each = local.private_ip
178   name = each.key
179   type = "A"
180   ttl = 300
181   records = [ each.value ]
182 }
183 resource "aws_route53_record" "private_main_a_record" {
184   zone_id = aws_route53_zone.private_zone.id
185   name = ""
186   type = "A"
187   ttl = 300
188   records = [ local.private_ip["controller"] ]
189 }
190 resource "aws_route53_record" "private_cname_record" {
191   zone_id = aws_route53_zone.private_zone.id
192   for_each = {for i in local.cname_by_host: i.record => "${i.cname}.${local.arvados_dns_zone}" }
193   name = each.key
194   type = "CNAME"
195   ttl = 300
196   records = [ each.value ]
197 }
198
199 #
200 # Route53's credentials for Let's Encrypt
201 #
202 resource "aws_iam_user" "letsencrypt" {
203   name = "${var.cluster_name}-letsencrypt"
204   path = "/"
205 }
206
207 resource "aws_iam_access_key" "letsencrypt" {
208   user = aws_iam_user.letsencrypt.name
209 }
210 resource "aws_iam_user_policy" "letsencrypt_iam_policy" {
211   name = "${var.cluster_name}-letsencrypt_iam_policy"
212   user = aws_iam_user.letsencrypt.name
213   policy = jsonencode({
214     "Version": "2012-10-17",
215     "Statement": [{
216       "Effect": "Allow",
217       "Action": [
218         "route53:ListHostedZones",
219         "route53:GetChange"
220       ],
221       "Resource": [
222           "*"
223       ]
224     },{
225       "Effect" : "Allow",
226       "Action" : [
227         "route53:ChangeResourceRecordSets"
228       ],
229       "Resource" : [
230         "arn:aws:route53:::hostedzone/${aws_route53_zone.public_zone.id}"
231       ]
232     }]
233   })
234 }
235