20482: Fixes use of var domain_name, it's now used for the Route53 zone.
[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" "public_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" "private_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" "compute_s3_route" {
46   vpc_endpoint_id = aws_vpc_endpoint.s3.id
47   route_table_id = aws_route_table.private_subnet_rt.id
48 }
49
50 #
51 # Internet access for Public IP instances
52 #
53 resource "aws_internet_gateway" "internet_gw" {
54   vpc_id = aws_vpc.arvados_vpc.id
55 }
56 resource "aws_eip" "arvados_eip" {
57   for_each = toset(local.public_hosts)
58   depends_on = [
59     aws_internet_gateway.internet_gw
60   ]
61 }
62 resource "aws_route_table" "public_subnet_rt" {
63   vpc_id = aws_vpc.arvados_vpc.id
64   route {
65     cidr_block = "0.0.0.0/0"
66     gateway_id = aws_internet_gateway.internet_gw.id
67   }
68 }
69 resource "aws_route_table_association" "public_subnet_assoc" {
70   subnet_id = aws_subnet.public_subnet.id
71   route_table_id = aws_route_table.public_subnet_rt.id
72 }
73
74 #
75 # Internet access for Private IP instances
76 #
77 resource "aws_eip" "nat_gw_eip" {
78   depends_on = [
79     aws_internet_gateway.internet_gw
80   ]
81 }
82 resource "aws_nat_gateway" "nat_gw" {
83   # A NAT gateway should be placed on a subnet with an internet gateway
84   subnet_id = aws_subnet.public_subnet.id
85   allocation_id = aws_eip.nat_gw_eip.id
86 }
87 resource "aws_route_table" "private_subnet_rt" {
88   vpc_id = aws_vpc.arvados_vpc.id
89   route {
90     cidr_block = "0.0.0.0/0"
91     nat_gateway_id = aws_nat_gateway.nat_gw.id
92   }
93 }
94 resource "aws_route_table_association" "private_subnet_assoc" {
95   subnet_id = aws_subnet.private_subnet.id
96   route_table_id = aws_route_table.private_subnet_rt.id
97 }
98
99 resource "aws_security_group" "arvados_sg" {
100   name = "arvados_sg"
101   vpc_id = aws_vpc.arvados_vpc.id
102
103   dynamic "ingress" {
104     for_each = local.allowed_ports
105     content {
106       description = "Ingress rule for ${ingress.key}"
107       from_port = "${ingress.value}"
108       to_port = "${ingress.value}"
109       protocol = "tcp"
110       cidr_blocks = ["0.0.0.0/0"]
111       ipv6_cidr_blocks = ["::/0"]
112     }
113   }
114   # Allows communication between nodes in the VPC
115   ingress {
116     from_port = 0
117     to_port = 0
118     protocol = "-1"
119     cidr_blocks = [ aws_vpc.arvados_vpc.cidr_block ]
120   }
121   # Even though AWS auto-creates an "allow all" egress rule,
122   # Terraform deletes it, so we add it explicitly.
123   egress {
124     from_port = 0
125     to_port = 0
126     protocol = "-1"
127     cidr_blocks = ["0.0.0.0/0"]
128     ipv6_cidr_blocks = ["::/0"]
129   }
130 }
131
132 #
133 # Route53 split-horizon DNS zones
134 #
135
136 # PUBLIC DNS
137 resource "aws_route53_zone" "public_zone" {
138   count = var.private_only ? 0 : 1
139   name = var.domain_name
140 }
141 resource "aws_route53_record" "public_a_record" {
142   zone_id = try(local.route53_public_zone.id, "")
143   for_each = local.public_ip
144   name = each.key
145   type = "A"
146   ttl = 300
147   records = [ each.value ]
148 }
149 resource "aws_route53_record" "main_a_record" {
150   count = var.private_only ? 0 : 1
151   zone_id = try(local.route53_public_zone.id, "")
152   name = ""
153   type = "A"
154   ttl = 300
155   records = [ local.public_ip["controller"] ]
156 }
157 resource "aws_route53_record" "public_cname_record" {
158   zone_id = try(local.route53_public_zone.id, "")
159   for_each = {
160     for i in local.cname_by_host: i.record =>
161       "${i.cname}.${var.domain_name}"
162     if var.private_only == false
163   }
164   name = each.key
165   type = "CNAME"
166   ttl = 300
167   records = [ each.value ]
168 }
169
170 # PRIVATE DNS
171 resource "aws_route53_zone" "private_zone" {
172   name = var.domain_name
173   vpc {
174     vpc_id = aws_vpc.arvados_vpc.id
175   }
176 }
177 resource "aws_route53_record" "private_a_record" {
178   zone_id = aws_route53_zone.private_zone.id
179   for_each = local.private_ip
180   name = each.key
181   type = "A"
182   ttl = 300
183   records = [ each.value ]
184 }
185 resource "aws_route53_record" "private_main_a_record" {
186   zone_id = aws_route53_zone.private_zone.id
187   name = ""
188   type = "A"
189   ttl = 300
190   records = [ local.private_ip["controller"] ]
191 }
192 resource "aws_route53_record" "private_cname_record" {
193   zone_id = aws_route53_zone.private_zone.id
194   for_each = {for i in local.cname_by_host: i.record => "${i.cname}.${var.domain_name}" }
195   name = each.key
196   type = "CNAME"
197   ttl = 300
198   records = [ each.value ]
199 }
200
201 #
202 # Route53's credentials for Let's Encrypt
203 #
204 resource "aws_iam_user" "letsencrypt" {
205   count = var.private_only ? 0 : 1
206   name = "${var.cluster_name}-letsencrypt"
207   path = "/"
208 }
209
210 resource "aws_iam_access_key" "letsencrypt" {
211   count = var.private_only ? 0 : 1
212   user = local.iam_user_letsencrypt.name
213 }
214 resource "aws_iam_user_policy" "letsencrypt_iam_policy" {
215   count = var.private_only ? 0 : 1
216   name = "${var.cluster_name}-letsencrypt_iam_policy"
217   user = local.iam_user_letsencrypt.name
218   policy = jsonencode({
219     "Version": "2012-10-17",
220     "Statement": [{
221       "Effect": "Allow",
222       "Action": [
223         "route53:ListHostedZones",
224         "route53:GetChange"
225       ],
226       "Resource": [
227           "*"
228       ]
229     },{
230       "Effect" : "Allow",
231       "Action" : [
232         "route53:ChangeResourceRecordSets"
233       ],
234       "Resource" : [
235         "arn:aws:route53:::hostedzone/${local.route53_public_zone.id}"
236       ]
237     }]
238   })
239 }
240