# Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: BUSL-1.1 resource "aws_instance" "vault_instance" { for_each = local.vault_instances ami = var.ami_id instance_type = var.instance_type vpc_security_group_ids = [aws_security_group.enos_vault_sg[0].id] subnet_id = tolist(data.aws_subnets.infra.ids)[each.key % length(data.aws_subnets.infra.ids)] key_name = var.ssh_aws_keypair iam_instance_profile = aws_iam_instance_profile.vault_profile[0].name ipv6_address_count = local.network_stack[var.ip_version].ipv6_address_count tags = merge( var.common_tags, { Name = "${local.name_suffix}-vault-${var.vault_node_prefix}-${each.key}-${split(":", data.aws_caller_identity.current.user_id)[1]}" Type = local.vault_cluster_tag }, ) } resource "enos_remote_exec" "install_dependencies" { depends_on = [aws_instance.vault_instance] for_each = toset([ for idx in local.vault_instances : idx if length(var.dependencies_to_install) > 0 ]) content = templatefile("${path.module}/templates/install-dependencies.sh", { dependencies = join(" ", var.dependencies_to_install) }) transport = { ssh = { host = var.ip_version == "6" ? aws_instance.vault_instance[each.value].ipv6_addresses[0] : aws_instance.vault_instance[each.value].public_ip } } } resource "enos_bundle_install" "consul" { for_each = { for idx, instance in aws_instance.vault_instance : idx => instance if var.storage_backend == "consul" } destination = var.consul_install_dir release = merge(var.consul_release, { product = "consul" }) transport = { ssh = { host = var.ip_version == "6" ? each.value.ipv6_addresses[0] : each.value.public_ip } } } resource "enos_bundle_install" "vault" { for_each = aws_instance.vault_instance destination = var.vault_install_dir release = var.vault_release == null ? var.vault_release : merge({ product = "vault" }, var.vault_release) artifactory = var.vault_artifactory_release path = var.vault_local_artifact_path transport = { ssh = { host = var.ip_version == "6" ? each.value.ipv6_addresses[0] : each.value.public_ip } } } resource "enos_consul_start" "consul" { for_each = enos_bundle_install.consul bin_path = local.consul_bin_path data_dir = var.consul_data_dir config = { data_dir = var.consul_data_dir datacenter = "dc1" retry_join = ["provider=aws tag_key=Type tag_value=${var.consul_cluster_tag}"] server = false bootstrap_expect = 0 license = var.consul_license log_level = var.consul_log_level log_file = var.consul_log_dir } license = var.consul_license unit_name = "consul" username = "consul" transport = { ssh = { host = var.ip_version == "6" ? aws_instance.vault_instance[each.key].ipv6_addresses[0] : aws_instance.vault_instance[each.key].public_ip } } } resource "enos_vault_start" "leader" { depends_on = [ enos_consul_start.consul, enos_bundle_install.vault, ] for_each = local.leader bin_path = local.vault_bin_path config_dir = var.vault_config_dir manage_service = var.manage_service config = { api_addr = var.ip_version == "4" ? "http://${aws_instance.vault_instance[each.key].private_ip}:8200" : "http://[${aws_instance.vault_instance[each.key].ipv6_addresses[0]}]:8200" cluster_addr = var.ip_version == "4" ? "http://${aws_instance.vault_instance[each.key].private_ip}:8201" : "http://[${aws_instance.vault_instance[each.key].ipv6_addresses[0]}]:8201" cluster_name = local.vault_cluster_tag listener = { type = "tcp" attributes = { address = var.ip_version == "4" ? "0.0.0.0:8200" : "[::]:8200" tls_disable = "true" } } log_level = var.vault_log_level storage = { type = var.storage_backend attributes = ({ for key, value in local.storage_config[each.key] : key => value }) } seal = local.seal[var.unseal_method] ui = true } license = var.vault_license unit_name = "vault" username = local.vault_service_user environment = var.vault_environment transport = { ssh = { host = var.ip_version == "6" ? aws_instance.vault_instance[each.key].ipv6_addresses[0] : aws_instance.vault_instance[each.key].public_ip } } } resource "enos_vault_start" "followers" { depends_on = [ enos_vault_start.leader, ] for_each = local.followers bin_path = local.vault_bin_path config_dir = var.vault_config_dir manage_service = var.manage_service config = { api_addr = var.ip_version == "4" ? "http://${aws_instance.vault_instance[each.key].private_ip}:8200" : "http://[${aws_instance.vault_instance[each.key].ipv6_addresses[0]}]:8200" cluster_addr = var.ip_version == "4" ? "http://${aws_instance.vault_instance[each.key].private_ip}:8201" : "http://[${aws_instance.vault_instance[each.key].ipv6_addresses[0]}]:8201" cluster_name = local.vault_cluster_tag log_level = var.vault_log_level listener = { type = "tcp" attributes = { address = var.ip_version == "4" ? "0.0.0.0:8200" : "[::]:8200" tls_disable = "true" } } storage = { type = var.storage_backend attributes = { for key, value in local.storage_config[each.key] : key => value } } seal = local.seal[var.unseal_method] ui = true } license = var.vault_license unit_name = "vault" username = local.vault_service_user environment = var.vault_environment transport = { ssh = { host = var.ip_version == "6" ? aws_instance.vault_instance[each.key].ipv6_addresses[0] : aws_instance.vault_instance[each.key].public_ip } } } resource "enos_vault_init" "leader" { depends_on = [enos_vault_start.followers] for_each = toset([ for idx in local.leader : idx if var.vault_init ]) bin_path = local.vault_bin_path vault_addr = enos_vault_start.leader[0].config.api_addr key_shares = local.key_shares[var.unseal_method] key_threshold = local.key_threshold[var.unseal_method] recovery_shares = local.recovery_shares[var.unseal_method] recovery_threshold = local.recovery_threshold[var.unseal_method] transport = { ssh = { host = var.ip_version == "6" ? aws_instance.vault_instance[0].ipv6_addresses[0] : aws_instance.vault_instance[0].public_ip } } } resource "enos_vault_unseal" "leader" { depends_on = [ enos_vault_start.followers, enos_vault_init.leader, ] for_each = enos_vault_init.leader // only unseal the leader if we initialized it bin_path = local.vault_bin_path vault_addr = enos_vault_start.leader[each.key].config.api_addr seal_type = var.unseal_method unseal_keys = var.unseal_method != "shamir" ? null : coalesce(var.vault_unseal_keys, enos_vault_init.leader[0].unseal_keys_hex) transport = { ssh = { host = var.ip_version == "6" ? aws_instance.vault_instance[0].ipv6_addresses[0] : aws_instance.vault_instance[0].public_ip } } } # We need to ensure that the directory used for audit logs is present and accessible to the vault # user on all nodes, since logging will only happen on the leader. resource "enos_remote_exec" "create_audit_log_dir" { depends_on = [ enos_vault_unseal.leader, ] for_each = toset([ for idx in local.vault_instances : idx if var.enable_file_audit_device ]) environment = { LOG_FILE_PATH = local.audit_device_file_path SERVICE_USER = local.vault_service_user } scripts = [abspath("${path.module}/scripts/create_audit_log_dir.sh")] transport = { ssh = { host = var.ip_version == "6" ? aws_instance.vault_instance[each.value].ipv6_addresses[0] : aws_instance.vault_instance[each.value].public_ip } } } resource "enos_remote_exec" "init_audit_device" { depends_on = [ enos_remote_exec.create_audit_log_dir, enos_vault_unseal.leader, ] for_each = toset([ for idx in local.leader : idx if local.enable_audit_device ]) environment = { VAULT_TOKEN = enos_vault_init.leader[each.key].root_token VAULT_ADDR = var.ip_version == "4" ? "http://127.0.0.1:8200" : "http://[::1]:8200" VAULT_BIN_PATH = local.vault_bin_path LOG_FILE_PATH = local.audit_device_file_path SERVICE_USER = local.vault_service_user } scripts = [ abspath("${path.module}/scripts/enable_audit_logging.sh"), ] transport = { ssh = { host = var.ip_version == "6" ? aws_instance.vault_instance[each.key].ipv6_addresses[0] : aws_instance.vault_instance[each.key].public_ip } } } resource "enos_vault_unseal" "followers" { depends_on = [ enos_vault_init.leader, enos_vault_unseal.leader, ] // Only unseal followers if we're not using an auto-unseal method and we've // initialized the cluster for_each = { for idx, follower in local.followers : idx => follower if var.unseal_method == "shamir" && var.vault_init } bin_path = local.vault_bin_path vault_addr = enos_vault_start.followers[each.key].config.api_addr seal_type = var.unseal_method unseal_keys = var.unseal_method != "shamir" ? null : coalesce(var.vault_unseal_keys, enos_vault_init.leader[0].unseal_keys_hex) transport = { ssh = { host = var.ip_version == "6" ? aws_instance.vault_instance[0].ipv6_addresses[0] : aws_instance.vault_instance[0].public_ip } } } // Unseal everything when we're not initializing and unseal when not init is set. // This flag is intended to handle cases when you're adding additional nodes to // an existing cluster to use auto-pilot to upgrade them and they're not using // an auto-unseal method. resource "enos_vault_unseal" "when_vault_unseal_when_no_init_is_set" { depends_on = [enos_vault_start.followers] for_each = toset([ for idx in local.instances : idx if var.vault_unseal_when_no_init && !var.vault_init ]) bin_path = local.vault_bin_path vault_addr = "http://localhost:8200" seal_type = var.unseal_method unseal_keys = coalesce( var.vault_unseal_keys, try(enos_vault_init.leader[0].unseal_keys_hex, null), ) transport = { ssh = { host = var.ip_version == "6" ? aws_instance.vault_instance[each.key].ipv6_addresses[0] : aws_instance.vault_instance[each.key].public_ip } } } resource "enos_remote_exec" "vault_write_license" { count = var.deploy && var.vault_init ? 1 : 0 depends_on = [ enos_vault_unseal.leader, enos_vault_unseal.when_vault_unseal_when_no_init_is_set, ] content = templatefile("${path.module}/templates/vault-write-license.sh", { vault_bin_path = local.vault_bin_path, vault_root_token = coalesce(var.vault_root_token, try(enos_vault_init.leader[0].root_token, null), "none") vault_license = coalesce(var.vault_license, "none") }) transport = { ssh = { host = var.ip_version == "6" ? aws_instance.vault_instance[0].ipv6_addresses[0] : aws_instance.vault_instance[0].public_ip } } } resource "enos_remote_exec" "vault_kms_policy" { count = var.deploy && var.vault_init ? 1 : 0 depends_on = [ enos_remote_exec.vault_write_license, ] content = templatefile("${path.module}/templates/create-kms-policy.sh", { vault_bin_path = local.vault_bin_path, vault_root_token = coalesce(var.vault_root_token, try(enos_vault_init.leader[0].root_token, null), "none") policy_path = "${path.module}/policies/kms-transit.hcl" }) transport = { ssh = { host = var.ip_version == "6" ? aws_instance.vault_instance[0].ipv6_addresses[0] : aws_instance.vault_instance[0].public_ip } } }