1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: CC-BY-SA-3.0
6 required_version = "~> 1.3.0"
9 source = "hashicorp/aws"
16 region = local.region_name
18 tags = merge(local.custom_tags, {
19 Arvados = local.cluster_name
27 resource "random_string" "default_rds_password" {
28 count = (local.use_rds && var.rds_password == "") ? 1 : 0
33 resource "aws_iam_instance_profile" "keepstore_instance_profile" {
34 name = "${local.cluster_name}-keepstore-00-iam-role"
35 role = data.terraform_remote_state.data-storage.outputs.keepstore_iam_role_name
38 resource "aws_iam_instance_profile" "compute_node_instance_profile" {
39 name = "${local.cluster_name}-compute-node-00-iam-role"
40 role = local.compute_node_iam_role_name
43 resource "aws_iam_instance_profile" "dispatcher_instance_profile" {
44 name = "${local.cluster_name}_dispatcher_instance_profile"
45 role = aws_iam_role.cloud_dispatcher_iam_role.name
48 resource "aws_secretsmanager_secret" "ssl_password_secret" {
49 name = local.ssl_password_secret_name
50 recovery_window_in_days = 0
53 resource "aws_iam_instance_profile" "default_instance_profile" {
54 name = "${local.cluster_name}_default_instance_profile"
55 role = aws_iam_role.default_iam_role.name
58 resource "aws_instance" "arvados_service" {
59 for_each = toset(concat(local.public_hosts, local.private_hosts))
60 ami = local.instance_ami_id
61 instance_type = try(var.instance_type[each.value], var.instance_type.default)
62 user_data = templatefile("user_data.sh", {
63 "hostname": each.value,
64 "deploy_user": var.deploy_user,
65 "ssh_pubkey": file(local.pubkey_path)
67 private_ip = local.private_ip[each.value]
68 subnet_id = contains(local.user_facing_hosts, each.value) ? local.public_subnet_id : local.private_subnet_id
69 vpc_security_group_ids = [ local.arvados_sg_id ]
70 iam_instance_profile = try(local.instance_profile[each.value], local.instance_profile.default).name
72 Name = "${local.cluster_name}_arvados_service_${each.value}"
76 volume_size = try(var.instance_volume_size[each.value], var.instance_volume_size.default)
79 # Sets IMDSv2 to required. Default is "optional".
80 http_tokens = "required"
81 http_endpoint = "enabled"
85 # Avoids recreating the instance when the latest AMI changes.
86 # Use 'terraform taint' or 'terraform apply -replace' to force
93 resource "aws_db_subnet_group" "arvados_db_subnet_group" {
94 count = local.use_rds ? 1 : 0
95 name = "${local.cluster_name}_db_subnet_group"
96 subnet_ids = [local.private_subnet_id, local.additional_rds_subnet_id]
99 resource "aws_db_instance" "postgresql_service" {
100 count = local.use_rds ? 1 : 0
101 allocated_storage = local.rds_allocated_storage
102 max_allocated_storage = local.rds_max_allocated_storage
104 engine_version = local.rds_postgresql_version
105 instance_class = local.rds_instance_type
106 db_name = "${local.cluster_name}_arvados"
107 username = local.rds_username
108 password = local.rds_password
109 skip_final_snapshot = !local.rds_backup_before_deletion
110 final_snapshot_identifier = local.rds_final_backup_name
112 vpc_security_group_ids = [local.arvados_sg_id]
113 db_subnet_group_name = aws_db_subnet_group.arvados_db_subnet_group[0].name
115 backup_retention_period = local.rds_backup_retention_period
116 publicly_accessible = false
117 storage_encrypted = true
127 Name = "${local.cluster_name}_postgresql_service"
131 resource "aws_iam_policy" "compute_node_ebs_autoscaler" {
132 name = "${local.cluster_name}_compute_node_ebs_autoscaler"
133 policy = jsonencode({
134 Version: "2012-10-17",
135 Id: "compute-node EBS Autoscaler policy",
140 "ec2:DescribeVolumeStatus",
141 "ec2:DescribeVolumes",
143 "ec2:ModifyInstanceAttribute",
144 "ec2:DescribeVolumeAttribute",
154 resource "aws_iam_policy_attachment" "compute_node_ebs_autoscaler_attachment" {
155 name = "${local.cluster_name}_compute_node_ebs_autoscaler_attachment"
156 roles = [ local.compute_node_iam_role_name ]
157 policy_arn = aws_iam_policy.compute_node_ebs_autoscaler.arn
161 resource "aws_iam_policy" "cmk_access" {
162 count = var.cmk_arn == "" ? 0 : 1
163 name = "${local.cluster_name}_cmk_access"
164 policy = jsonencode({
165 Version: "2012-10-17",
172 "kms:GenerateDataKey*"
180 Action: "kms:CreateGrant",
186 "kms:GrantIsForAWSResource": true
193 resource "aws_iam_policy_attachment" "compute_node_cmk_access_attachment" {
194 count = var.cmk_arn == "" ? 0 : 1
195 name = "${local.cluster_name}_compute_node_cmk_access_attachment"
196 roles = [ local.compute_node_iam_role_name ]
197 policy_arn = aws_iam_policy.cmk_access[0].arn
200 resource "aws_iam_policy_attachment" "dispatcher_cmk_access_attachment" {
201 count = var.cmk_arn == "" ? 0 : 1
202 name = "${local.cluster_name}_dispatchercmk_access_attachment"
203 roles = [ aws_iam_role.cloud_dispatcher_iam_role.name ]
204 policy_arn = aws_iam_policy.cmk_access[0].arn
207 resource "aws_iam_policy" "cloud_dispatcher_ec2_access" {
208 name = "${local.cluster_name}_cloud_dispatcher_ec2_access"
209 policy = jsonencode({
210 Version: "2012-10-17",
211 Id: "arvados-dispatch-cloud policy",
215 "ec2:DescribeKeyPairs",
218 "ec2:DescribeInstances",
220 "ec2:TerminateInstances"
229 Resource: "arn:aws:iam::*:role/${aws_iam_instance_profile.compute_node_instance_profile.name}"
234 resource "aws_iam_role" "cloud_dispatcher_iam_role" {
235 name = "${local.cluster_name}-dispatcher-00-iam-role"
236 assume_role_policy = "${file("../assumerolepolicy.json")}"
239 resource "aws_iam_policy_attachment" "cloud_dispatcher_ec2_access_attachment" {
240 name = "${local.cluster_name}_cloud_dispatcher_ec2_access_attachment"
241 roles = [ aws_iam_role.cloud_dispatcher_iam_role.name ]
242 policy_arn = aws_iam_policy.cloud_dispatcher_ec2_access.arn
245 resource "aws_eip_association" "eip_assoc" {
246 for_each = local.private_only ? [] : toset(local.public_hosts)
247 instance_id = aws_instance.arvados_service[each.value].id
248 allocation_id = local.eip_id[each.value]
251 resource "aws_iam_role" "default_iam_role" {
252 name = "${local.cluster_name}-default-iam-role"
253 assume_role_policy = "${file("../assumerolepolicy.json")}"
256 resource "aws_iam_policy" "ssl_privkey_password_access" {
257 name = "${local.cluster_name}_ssl_privkey_password_access"
258 policy = jsonencode({
259 Version: "2012-10-17",
262 Action: "secretsmanager:GetSecretValue",
263 Resource: "${aws_secretsmanager_secret.ssl_password_secret.arn}"
268 # Every service node needs access to the SSL privkey password secret for
269 # nginx to be able to use it.
270 resource "aws_iam_policy_attachment" "ssl_privkey_password_access_attachment" {
271 name = "${local.cluster_name}_ssl_privkey_password_access_attachment"
273 aws_iam_role.cloud_dispatcher_iam_role.name,
274 aws_iam_role.default_iam_role.name,
275 local.keepstore_iam_role_name,
277 policy_arn = aws_iam_policy.ssl_privkey_password_access.arn