21751: Adds CMK access policies to compute and dispatcher roles.
[arvados.git] / tools / salt-install / terraform / aws / services / 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 = local.region_name
17   default_tags {
18     tags = merge(local.custom_tags, {
19       Arvados = local.cluster_name
20       Terraform = true
21     })
22   }
23 }
24
25 provider "random" {}
26
27 resource "random_string" "default_rds_password" {
28   count = (local.use_rds && var.rds_password == "") ? 1 : 0
29   length  = 32
30   special = false
31 }
32
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
36 }
37
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
41 }
42
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
46 }
47
48 resource "aws_secretsmanager_secret" "ssl_password_secret" {
49   name = local.ssl_password_secret_name
50   recovery_window_in_days = 0
51 }
52
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
56 }
57
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)
66   })
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
71   tags = {
72     Name = "${local.cluster_name}_arvados_service_${each.value}"
73   }
74   root_block_device {
75     volume_type = "gp3"
76     volume_size = try(var.instance_volume_size[each.value], var.instance_volume_size.default)
77   }
78   metadata_options {
79     # Sets IMDSv2 to required. Default is "optional".
80     http_tokens = "required"
81     http_endpoint = "enabled"
82   }
83   lifecycle {
84     ignore_changes = [
85       # Avoids recreating the instance when the latest AMI changes.
86       # Use 'terraform taint' or 'terraform apply -replace' to force
87       # an AMI change.
88       ami,
89     ]
90   }
91 }
92
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]
97 }
98
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
103   engine = "postgres"
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
111
112   vpc_security_group_ids = [local.arvados_sg_id]
113   db_subnet_group_name = aws_db_subnet_group.arvados_db_subnet_group[0].name
114
115   backup_retention_period = local.rds_backup_retention_period
116   publicly_accessible = false
117   storage_encrypted = true
118   multi_az = false
119
120   lifecycle {
121     ignore_changes = [
122       username,
123     ]
124   }
125
126   tags = {
127     Name = "${local.cluster_name}_postgresql_service"
128   }
129 }
130
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",
136     Statement: [{
137       Effect: "Allow",
138       Action: [
139           "ec2:AttachVolume",
140           "ec2:DescribeVolumeStatus",
141           "ec2:DescribeVolumes",
142           "ec2:DescribeTags",
143           "ec2:ModifyInstanceAttribute",
144           "ec2:DescribeVolumeAttribute",
145           "ec2:CreateVolume",
146           "ec2:DeleteVolume",
147           "ec2:CreateTags"
148       ],
149       Resource: "*"
150     }]
151   })
152 }
153
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
158 }
159
160
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",
166     Statement: [{
167       Effect: "Allow",
168       Action: [
169         "kms:Encrypt",
170         "kms:Decrypt",
171         "kms:DescribeKey",
172         "kms:GenerateDataKey*"
173       ],
174       Resource: [
175         var.cmk_arn
176       ]
177     },
178     {
179       Effect: "Allow",
180       Action: "kms:CreateGrant",
181       Resource: [
182         var.cmk_arn
183       ],
184       Condition: {
185         Bool: {
186           "kms:GrantIsForAWSResource": true
187         }
188       }
189     }]
190   })
191 }
192
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
198 }
199
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
205 }
206
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",
212     Statement: [{
213       Effect: "Allow",
214       Action: [
215         "ec2:DescribeKeyPairs",
216         "ec2:ImportKeyPair",
217         "ec2:RunInstances",
218         "ec2:DescribeInstances",
219         "ec2:CreateTags",
220         "ec2:TerminateInstances"
221       ],
222       Resource: "*"
223     },
224     {
225       Effect: "Allow",
226       Action: [
227         "iam:PassRole",
228       ],
229       Resource: "arn:aws:iam::*:role/${aws_iam_instance_profile.compute_node_instance_profile.name}"
230     }]
231   })
232 }
233
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")}"
237 }
238
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
243 }
244
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]
249 }
250
251 resource "aws_iam_role" "default_iam_role" {
252   name = "${local.cluster_name}-default-iam-role"
253   assume_role_policy = "${file("../assumerolepolicy.json")}"
254 }
255
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",
260     Statement: [{
261       Effect: "Allow",
262       Action: "secretsmanager:GetSecretValue",
263       Resource: "${aws_secretsmanager_secret.ssl_password_secret.arn}"
264     }]
265   })
266 }
267
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"
272   roles = [
273     aws_iam_role.cloud_dispatcher_iam_role.name,
274     aws_iam_role.default_iam_role.name,
275     local.keepstore_iam_role_name,
276   ]
277   policy_arn = aws_iam_policy.ssl_privkey_password_access.arn
278 }