1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: CC-BY-SA-3.0
8 source = "hashicorp/aws"
14 region = var.region_name
17 Arvados = var.cluster_name
22 resource "aws_vpc" "arvados_vpc" {
23 cidr_block = "10.1.0.0/16"
24 enable_dns_hostnames = true
25 enable_dns_support = true
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"
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"
41 resource "aws_vpc_endpoint" "s3" {
42 vpc_id = aws_vpc.arvados_vpc.id
43 service_name = "com.amazonaws.${var.region_name}.s3"
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
51 # Internet access for Public IP instances
53 resource "aws_internet_gateway" "internet_gw" {
54 vpc_id = aws_vpc.arvados_vpc.id
56 resource "aws_eip" "arvados_eip" {
57 for_each = toset(local.public_hosts)
59 aws_internet_gateway.internet_gw
62 resource "aws_route_table" "public_subnet_rt" {
63 vpc_id = aws_vpc.arvados_vpc.id
65 cidr_block = "0.0.0.0/0"
66 gateway_id = aws_internet_gateway.internet_gw.id
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
75 # Internet access for Private IP instances
77 resource "aws_eip" "nat_gw_eip" {
79 aws_internet_gateway.internet_gw
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
87 resource "aws_route_table" "private_subnet_rt" {
88 vpc_id = aws_vpc.arvados_vpc.id
90 cidr_block = "0.0.0.0/0"
91 nat_gateway_id = aws_nat_gateway.nat_gw.id
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
99 resource "aws_security_group" "arvados_sg" {
101 vpc_id = aws_vpc.arvados_vpc.id
104 for_each = local.allowed_ports
106 description = "Ingress rule for ${ingress.key}"
107 from_port = "${ingress.value}"
108 to_port = "${ingress.value}"
110 cidr_blocks = ["0.0.0.0/0"]
111 ipv6_cidr_blocks = ["::/0"]
114 # Allows communication between nodes in the VPC
119 cidr_blocks = [ aws_vpc.arvados_vpc.cidr_block ]
121 # Even though AWS auto-creates an "allow all" egress rule,
122 # Terraform deletes it, so we add it explicitly.
127 cidr_blocks = ["0.0.0.0/0"]
128 ipv6_cidr_blocks = ["::/0"]
133 # Route53 split-horizon DNS zones
137 resource "aws_route53_zone" "public_zone" {
138 count = var.private_only ? 0 : 1
139 name = var.domain_name
141 resource "aws_route53_record" "public_a_record" {
142 zone_id = try(local.route53_public_zone.id, "")
143 for_each = local.public_ip
147 records = [ each.value ]
149 resource "aws_route53_record" "main_a_record" {
150 count = var.private_only ? 0 : 1
151 zone_id = try(local.route53_public_zone.id, "")
155 records = [ local.public_ip["controller"] ]
157 resource "aws_route53_record" "public_cname_record" {
158 zone_id = try(local.route53_public_zone.id, "")
160 for i in local.cname_by_host: i.record =>
161 "${i.cname}.${var.domain_name}"
162 if var.private_only == false
167 records = [ each.value ]
171 resource "aws_route53_zone" "private_zone" {
172 name = var.domain_name
174 vpc_id = aws_vpc.arvados_vpc.id
177 resource "aws_route53_record" "private_a_record" {
178 zone_id = aws_route53_zone.private_zone.id
179 for_each = local.private_ip
183 records = [ each.value ]
185 resource "aws_route53_record" "private_main_a_record" {
186 zone_id = aws_route53_zone.private_zone.id
190 records = [ local.private_ip["controller"] ]
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}" }
198 records = [ each.value ]
202 # Route53's credentials for Let's Encrypt
204 resource "aws_iam_user" "letsencrypt" {
205 count = var.private_only ? 0 : 1
206 name = "${var.cluster_name}-letsencrypt"
210 resource "aws_iam_access_key" "letsencrypt" {
211 count = var.private_only ? 0 : 1
212 user = local.iam_user_letsencrypt.name
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",
223 "route53:ListHostedZones",
232 "route53:ChangeResourceRecordSets"
235 "arn:aws:route53:::hostedzone/${local.route53_public_zone.id}"